From bee5dd259a4438d45ecd1bcc26dfba12875236d6 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Tue, 26 Jun 2018 11:03:48 +0200 Subject: initial commit --- .../metadata/AbstractChainingMetadataProvider.java | 446 +++++++++++++++++++++ 1 file changed, 446 insertions(+) create mode 100644 eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/AbstractChainingMetadataProvider.java (limited to 'eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/AbstractChainingMetadataProvider.java') diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/AbstractChainingMetadataProvider.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/AbstractChainingMetadataProvider.java new file mode 100644 index 00000000..c3eaa9a3 --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/AbstractChainingMetadataProvider.java @@ -0,0 +1,446 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eaaf.modules.pvp2.impl.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.Timer; + +import javax.xml.namespace.QName; + +import org.apache.commons.lang3.StringUtils; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.eaaf.core.api.IDestroyableObject; +import at.gv.egiz.eaaf.core.api.IGarbageCollectorProcessing; +import at.gv.egiz.eaaf.core.exceptions.EAAFConfigurationException; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPVPMetadataProvider; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IRefreshableMetadataProvider; + +public abstract class AbstractChainingMetadataProvider extends SimpleMetadataProvider + implements ObservableMetadataProvider, IGarbageCollectorProcessing, + IRefreshableMetadataProvider, IDestroyableObject, IPVPMetadataProvider { + + private static final Logger log = LoggerFactory.getLogger(AbstractChainingMetadataProvider.class); + + private MetadataProvider internalProvider = null; + private static Object mutex = new Object(); + private Timer timer = null; + + + public AbstractChainingMetadataProvider() { + internalProvider = new ChainingMetadataProvider(); + + } + + public final Timer getTimer() { + return this.timer; + + } + + /* (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 { + log.trace("Check consistence of PVP2X metadata"); + addAndRemoveMetadataProvider(); + + } catch (EAAFConfigurationException e) { + log.error("Access to MOA-ID configuration FAILED.", e); + + } + } + + } + + public void fullyDestroy() { + internalDestroy(); + + } + + @Override + public synchronized boolean refreshMetadataProvider(String entityID) { + try { + //check if metadata provider is already loaded + try { + if (internalProvider.getEntityDescriptor(entityID) != null) + return true; + + } catch (MetadataProviderException e) {} + + + //reload metadata provider + String metadataURL = getMetadataURL(entityID); + if (StringUtils.isNotEmpty(metadataURL)) { + Map actuallyLoadedProviders = getAllActuallyLoadedProviders(); + + // check if MetadataProvider is actually loaded + if (actuallyLoadedProviders.containsKey(metadataURL)) { + actuallyLoadedProviders.get(metadataURL).refresh(); + log.info("SAML2 metadata for service provider: " + + entityID + " is refreshed."); + return true; + + } else { + //load new Metadata Provider + if (timer == null) + timer = new Timer(true); + + ChainingMetadataProvider chainProvider = (ChainingMetadataProvider) internalProvider; + chainProvider.addMetadataProvider(createNewMetadataProvider(entityID)); + + emitChangeEvent(); + log.info("SAML2 metadata for service provider: " + + entityID + " is added."); + return true; + + } + + } else + log.debug("Can not refresh SAML2 metadata: NO SAML2 metadata URL for SP with Id: " + entityID); + + } catch (MetadataProviderException e) { + log.warn("Refresh SAML2 metadata for service provider: " + + entityID + " FAILED.", e); + + } catch (IOException e) { + log.warn("Refresh SAML2 metadata for service provider: " + + entityID + " FAILED.", e); + + } catch (EAAFConfigurationException e) { + log.warn("Refresh SAML2 metadata for service provider: " + + entityID + " FAILED.", e); + + } catch (CertificateException e) { + log.warn("Refresh SAML2 metadata for service provider: " + + entityID + " FAILED.", e); + + } + + return false; + + } + + public void internalDestroy() { + if (internalProvider != null && internalProvider instanceof ChainingMetadataProvider) { + log.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; + log.debug("Destroy HTTPMetadataProvider +" + httpprovider.getMetadataURI()); + httpprovider.destroy(); + + } else { + log.warn("MetadataProvider can not be destroyed."); + } + } + + internalProvider = new ChainingMetadataProvider(); + + if (timer != null) + timer.cancel(); + + } else { + log.warn("ReInitalize MOAMetaDataProvider is not possible! MOA-ID Instance has to be restarted manualy"); + } + } + + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.pvp2x.metadata.IEAAFMetadataProvider#requireValidMetadata() + */ + @Override + public boolean requireValidMetadata() { + return internalProvider.requireValidMetadata(); + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.pvp2x.metadata.IEAAFMetadataProvider#setRequireValidMetadata(boolean) + */ + @Override + public void setRequireValidMetadata(boolean requireValidMetadata) { + internalProvider.setRequireValidMetadata(requireValidMetadata); + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.pvp2x.metadata.IEAAFMetadataProvider#getMetadataFilter() + */ + @Override + public MetadataFilter getMetadataFilter() { + return internalProvider.getMetadataFilter(); + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.pvp2x.metadata.IEAAFMetadataProvider#setMetadataFilter(org.opensaml.saml2.metadata.provider.MetadataFilter) + */ + @Override + public void setMetadataFilter(MetadataFilter newFilter) + throws MetadataProviderException { + internalProvider.setMetadataFilter(newFilter); + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.pvp2x.metadata.IEAAFMetadataProvider#getMetadata() + */ + @Override + public XMLObject getMetadata() throws MetadataProviderException { + return internalProvider.getMetadata(); + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.pvp2x.metadata.IEAAFMetadataProvider#getEntitiesDescriptor(java.lang.String) + */ + @Override + public EntitiesDescriptor getEntitiesDescriptor(String entitiesID) + throws MetadataProviderException { + EntitiesDescriptor entitiesDesc = null; + try { + entitiesDesc = internalProvider.getEntitiesDescriptor(entitiesID); + + if (entitiesDesc == null) { + log.debug("Can not find PVP metadata for entityID: " + entitiesID + + " Start refreshing process ..."); + if (refreshMetadataProvider(entitiesID)) + return internalProvider.getEntitiesDescriptor(entitiesID); + + } + + } catch (MetadataProviderException e) { + log.debug("Can not find PVP metadata for entityID: " + entitiesID + + " Start refreshing process ..."); + if (refreshMetadataProvider(entitiesID)) + return internalProvider.getEntitiesDescriptor(entitiesID); + + } + + return entitiesDesc; + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.pvp2x.metadata.IEAAFMetadataProvider#getEntityDescriptor(java.lang.String) + */ + @Override + public EntityDescriptor getEntityDescriptor(String entityID) + throws MetadataProviderException { + EntityDescriptor entityDesc = null; + try { + entityDesc = internalProvider.getEntityDescriptor(entityID); + if (entityDesc == null) { + log.debug("Can not find PVP metadata for entityID: " + entityID + + " Start refreshing process ..."); + if (refreshMetadataProvider(entityID)) + return internalProvider.getEntityDescriptor(entityID); + + } + + } catch (MetadataProviderException e) { + log.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; + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.pvp2x.metadata.IEAAFMetadataProvider#getRole(java.lang.String, javax.xml.namespace.QName) + */ + @Override + 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; + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.pvp2x.metadata.IEAAFMetadataProvider#getRole(java.lang.String, javax.xml.namespace.QName, java.lang.String) + */ + @Override + 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(); + } + + + /** + * Get the URL to metadata for a specific entityID + * + * @param entityId + * @return + * @throws EAAFConfigurationException + */ + protected abstract String getMetadataURL(String entityId) throws EAAFConfigurationException; + + /** + * Creates a new implementation specific SAML2 metadata provider + * + * @param entityId + * @return + * @throws EAAFConfigurationException + * @throws IOException + * @throws CertificateException + * @throws ConfigurationException + */ + protected abstract MetadataProvider createNewMetadataProvider(String entityId) throws EAAFConfigurationException, IOException, CertificateException; + + /** + * Get a List of metadata URLs for all SAML2 SPs from configuration + * + * @throws EAAFConfigurationException + */ + protected abstract List getAllMetadataURLsFromConfiguration() throws EAAFConfigurationException; + + + 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); + } + + 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 EAAFConfigurationException { + if (internalProvider != null && internalProvider instanceof ChainingMetadataProvider) { + log.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! + */ + + //load all SAML2 SPs form configuration and + //compare actually loaded Providers with configured SAML2 SPs + List allMetadataURLs = getAllMetadataURLsFromConfiguration(); + + if (allMetadataURLs != null) { + Iterator metadataURLInterator = allMetadataURLs.iterator(); + while (metadataURLInterator.hasNext()) { + String metadataurl = metadataURLInterator.next(); + try { + if (StringUtils.isNotEmpty(metadataurl)) { + if (loadedproviders.containsKey(metadataurl)) { + // SAML2 SP is actually loaded, to nothing + providersinuse.put(metadataurl, loadedproviders.get(metadataurl)); + loadedproviders.remove(metadataurl); + + } + } + } catch (Throwable e) { + log.error( + "Failed to add Metadata (unhandled reason: " + e.getMessage(), e); + + } + } + } + + //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); + log.info("Remove not used MetadataProvider with MetadataURL " + metadataurl); + + } catch (Throwable e) { + log.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) { + log.warn("ReInitalize AbstractMetaDataProvider is not possible! Service has to be restarted manualy", e); + + } + + } else + log.warn("ReInitalize AbstractMetaDataProvider is not possible! Service has to be restarted manualy"); + + } +} -- cgit v1.2.3