package at.gv.egovernment.moa.id.auth.modules.eidas.engine; import java.util.ArrayList; import java.util.Date; 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.FilesystemMetadataProvider; 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.auth.IPostStartupInitializable; import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; import at.gv.egovernment.moa.id.protocols.pvp2x.metadata.IMOARefreshableMetadataProvider; import at.gv.egovernment.moa.id.protocols.pvp2x.metadata.SimpleMOAMetadataProvider; import at.gv.egovernment.moa.id.protocols.pvp2x.verification.metadata.MOASPMetadataSignatureFilter; import at.gv.egovernment.moa.id.saml2.MetadataFilterChain; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.FileUtils; import at.gv.egovernment.moa.util.MiscUtil; import eu.eidas.auth.engine.AbstractProtocolEngine; @Service("eIDASMetadataProvider") public class MOAeIDASChainingMetadataProvider extends SimpleMOAMetadataProvider implements ObservableMetadataProvider, IGarbageCollectorProcessing, IDestroyableObject, IMOARefreshableMetadataProvider, IPostStartupInitializable{ private Timer timer = null; private MetadataProvider internalProvider; private Map lastAccess = null; // public static MOAeIDASChainingMetadataProvider getInstance() { // if (instance == null) { // synchronized (mutex) { // if (instance == null) { // instance = new MOAeIDASChainingMetadataProvider(); // MOAGarbageCollector.addModulForGarbageCollection(instance); // } // } // } // return instance; // } public MOAeIDASChainingMetadataProvider() { internalProvider = new ChainingMetadataProvider(); lastAccess = new HashMap(); } /* (non-Javadoc) * @see at.gv.egovernment.moa.id.auth.IPostStartupInitializable#executeAfterStartup() */ @Override public void executeAfterStartup() { initializeEidasMetadataFromFileSystem(); } protected void initializeEidasMetadataFromFileSystem() { Map metadataToLoad = authConfig.getBasicMOAIDConfigurationWithPrefix(Constants.CONIG_PROPS_EIDAS_METADATA_URLS_LIST_PREFIX); if (!metadataToLoad.isEmpty()) { Logger.info("Load static configurated eIDAS metadata ... "); for (String metaatalocation : metadataToLoad.values()) { String absMetadataLocation = FileUtils.makeAbsoluteURL(metaatalocation, authConfig.getRootConfigFileDir()); Logger.info(" Load eIDAS metadata from: " + absMetadataLocation); refreshMetadataProvider(absMetadataLocation); } Logger.info("Load static configurated eIDAS metadata finished "); } } /* (non-Javadoc) * @see at.gv.egovernment.moa.id.auth.IDestroyableObject#fullyDestroy() */ @Override public void fullyDestroy() { if (timer != null) timer.cancel(); Map loadedproviders = getAllActuallyLoadedProviders(); if (loadedproviders != null) { for (Entry el : loadedproviders.entrySet()) { try { el.getValue().destroy(); Logger.debug("Destroy eIDAS Matadataprovider: " + el.getKey() + " finished"); } catch (Exception e) { Logger.warn("Destroy eIDAS Matadataprovider: " + el.getKey() + " FAILED"); } } } } /* (non-Javadoc) * @see at.gv.egovernment.moa.id.config.auth.IGarbageCollectorProcessing#runGarbageCollector() */ @Override public void runGarbageCollector() { if (!lastAccess.isEmpty()) { Date now = new Date(); Date expioredate = new Date(now.getTime() - Constants.CONFIG_PROPS_METADATA_GARBAGE_TIMEOUT); Logger.debug("Starting eIDAS Metadata garbag collection (Expioredate:" + expioredate + ")"); List expiredEntities = new ArrayList(); Iterator> lastAccessInterator = lastAccess.entrySet().iterator(); while(lastAccessInterator.hasNext()) { Entry element = lastAccessInterator.next(); if (element.getValue().before(expioredate)) { Logger.debug("Remove unused eIDAS Metadate: " + element.getKey()); expiredEntities.add(element.getKey()); } } ChainingMetadataProvider chainProvider = (ChainingMetadataProvider) internalProvider; boolean isUpdateRequired = false; //get all actually loaded metadata providers Map loadedproviders = getAllActuallyLoadedProviders(); if (!expiredEntities.isEmpty()) { for (String expired : expiredEntities) { if (loadedproviders.containsKey(expired)) { HTTPMetadataProvider provider = loadedproviders.get(expired); //destroy metadata provider provider.destroy(); //remove from map loadedproviders.remove(expired); isUpdateRequired = true; /*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 eIDAS MetadataProvider " + expired + " after timeout."); } else Logger.info("eIDAS metadata for EntityID: " + expired + " is marked as expired, but no currently loaded HTTPMetadataProvider metadata provider is found."); } } //check signature of all metadata which are actually loaded List nonValidMetadataProvider = new ArrayList(); for (HTTPMetadataProvider provider : loadedproviders.values()) { try { provider.refresh(); //provider.getMetadataFilter().doFilter(provider.getMetadata()); } catch (MetadataProviderException e) { Logger.info("eIDAS MetadataProvider: " + provider.getMetadataURI() + " is not valid any more. Reason:" + e.getMessage()); if (Logger.isDebugEnabled()) Logger.warn("Reason", e); nonValidMetadataProvider.add(provider.getMetadataURI()); } } for (String el : nonValidMetadataProvider) { HTTPMetadataProvider provider = loadedproviders.get(el); //destroy metadata provider if (provider != null) { provider.destroy(); loadedproviders.remove(el); isUpdateRequired = true; } else { Logger.error("Can not destroy eIDAS metadata for: " + el + " Reason: !!!!!NOT FOUND ANY MORE!!!!!!"); } } //update chaining metadata-provider if it is required if (isUpdateRequired) { try { synchronized (chainProvider) { chainProvider.setProviders(new ArrayList(loadedproviders.values())); emitChangeEvent(); } } catch (MetadataProviderException e) { Logger.warn("ReInitalize eIDASA MetaDataProvider is not possible! MOA-ID Instance has to be restarted manualy", e); } } } } private MetadataProvider createNewHTTPMetaDataProvider(String metadataURL) { if (timer == null) timer = new Timer(true); //add Metadata filters MetadataFilterChain filter = new MetadataFilterChain(); filter.addFilter(new MOASPMetadataSignatureFilter( authConfig.getBasicMOAIDConfiguration(Constants.CONIG_PROPS_EIDAS_METADATA_VALIDATION_TRUSTSTORE))); return createNewMoaMetadataProvider(metadataURL, filter, "eIDAS metadata-provider", timer, AbstractProtocolEngine.getSecuredParserPool()); } 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); } else if (provider instanceof FilesystemMetadataProvider) { String entityID = "'!!NO-ENTITYID!!'"; try { if (provider.getMetadata() instanceof EntityDescriptor) entityID = ((EntityDescriptor)provider.getMetadata()).getEntityID(); Logger.debug("Skip eIDAS metadata: " + entityID + " because it is loaded from local Filesystem"); } catch (MetadataProviderException e) { Logger.info("Collect currently loaded eIDAS metadata provider has an internel process error: " + e.getMessage()); } } else Logger.info("Skip " + provider.getClass().getName() + " from list of currently loaded " + "eIDAS metadata provider"); } Logger.debug("Find #" + loadedproviders.size() + " eIDAS metadata provider"); return loadedproviders; } public boolean refreshMetadataProvider(String metadataURL) { try { if (MiscUtil.isNotEmpty(metadataURL)) { Map actuallyLoadedProviders = getAllActuallyLoadedProviders(); // check if MetadataProvider is actually loaded if (actuallyLoadedProviders.containsKey(metadataURL)) { actuallyLoadedProviders.get(metadataURL).refresh(); Logger.info("eIDAS metadata for " + metadataURL + " is refreshed."); return true; } else { //load new Metadata Provider ChainingMetadataProvider chainProvider = (ChainingMetadataProvider) internalProvider; MetadataProvider newMetadataProvider = createNewHTTPMetaDataProvider(metadataURL); if (newMetadataProvider != null) { chainProvider.addMetadataProvider(newMetadataProvider); emitChangeEvent(); Logger.info("eIDAS metadata for " + metadataURL + " is added."); return true; } else Logger.warn("Can not load eIDAS metadata from URL: " + metadataURL); } } else Logger.debug("Can not refresh eIDAS metadata: NO eIDAS metadata URL."); } catch (MetadataProviderException e) { Logger.warn("Refresh eIDAS metadata for " + metadataURL + " FAILED.", e); } return false; } 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 { Logger.warn("eIDAS metadata not support 'EntitiesDescriptor' elements!"); return null; } public EntityDescriptor getEntityDescriptor(String entityID) throws MetadataProviderException { EntityDescriptor entityDesc = null; try { entityDesc = internalProvider.getEntityDescriptor(entityID); if (entityDesc == null) { Logger.debug("Can not find eIDAS metadata for entityID: " + entityID + " Start refreshing process ..."); if (refreshMetadataProvider(entityID)) entityDesc = internalProvider.getEntityDescriptor(entityID); } else { if (!entityDesc.isValid()) if (refreshMetadataProvider(entityID)) entityDesc = internalProvider.getEntityDescriptor(entityID); } } catch (MetadataProviderException e) { Logger.debug("Can not find eIDAS metadata for entityID: " + entityID + " Start refreshing process ..."); if (refreshMetadataProvider(entityID)) entityDesc = internalProvider.getEntityDescriptor(entityID); } if (entityDesc != null) lastAccess.put(entityID, new Date()); return entityDesc; } public List getRole(String entityID, QName roleName) throws MetadataProviderException { EntityDescriptor entityDesc = getEntityDescriptor(entityID); if (entityDesc != null) return entityDesc.getRoleDescriptors(roleName); else return null; } public RoleDescriptor getRole(String entityID, QName roleName, String supportedProtocol) throws MetadataProviderException { EntityDescriptor entityDesc = getEntityDescriptor(entityID); if (entityDesc != null) return internalProvider.getRole(entityID, roleName, supportedProtocol); else return null; } /* (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); } }