diff options
Diffstat (limited to 'eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/AbstractChainingMetadataProvider.java')
-rw-r--r-- | eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/AbstractChainingMetadataProvider.java | 960 |
1 files changed, 510 insertions, 450 deletions
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 index 08ef26ab..40448b45 100644 --- 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 @@ -1,470 +1,530 @@ -/******************************************************************************* - * Copyright 2017 Graz University of Technology - * EAAF-Core Components has been developed in a cooperation between EGIZ, - * A-SIT Plus, A-SIT, and Graz University of Technology. +/* + * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a + * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * - * Licensed under the EUPL, Version 1.2 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: + * Licensed under the EUPL, Version 1.2 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: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * - * 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. - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ + * 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.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.Collections; 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 javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.naming.ConfigurationException; import at.gv.egiz.components.spring.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.core.exceptions.EaafConfigurationException; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvpAddableChainingMetadataProvider; 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<String, HTTPMetadataProvider> 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<MetadataProvider> 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<RoleDescriptor> getRole(String entityID, QName roleName) - throws MetadataProviderException { - List<RoleDescriptor> 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<Observer> 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<String> getAllMetadataURLsFromConfiguration() throws EAAFConfigurationException; - - - protected void emitChangeEvent() { - if ((getObservers() == null) || (getObservers().size() == 0)) { - return; - } - - List<Observer> tempObserverList = new ArrayList<Observer>(getObservers()); - for (ObservableMetadataProvider.Observer observer : tempObserverList) - if (observer != null) - observer.onEvent(this); - } - - private Map<String, HTTPMetadataProvider> getAllActuallyLoadedProviders() { - 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); - - } - } - - 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<String, MetadataProvider> providersinuse = new HashMap<String, MetadataProvider>(); - ChainingMetadataProvider chainProvider = (ChainingMetadataProvider) internalProvider; - - //get all actually loaded metadata providers - Map<String, HTTPMetadataProvider> 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<String> allMetadataURLs = getAllMetadataURLsFromConfiguration(); - - if (allMetadataURLs != null) { - Iterator<String> 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<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); - 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<MetadataProvider>(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"); - - } +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.opensaml.core.criterion.EntityIdCriterion; +import org.opensaml.saml.metadata.resolver.ClearableMetadataResolver; +import org.opensaml.saml.metadata.resolver.MetadataResolver; +import org.opensaml.saml.metadata.resolver.RefreshableMetadataResolver; +import org.opensaml.saml.metadata.resolver.filter.MetadataFilter; +import org.opensaml.saml.metadata.resolver.impl.AbstractMetadataResolver; +import org.opensaml.saml.saml2.metadata.EntityDescriptor; + +import lombok.extern.slf4j.Slf4j; +import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements; +import net.shibboleth.utilities.java.support.component.IdentifiedComponent; +import net.shibboleth.utilities.java.support.resolver.CriteriaSet; +import net.shibboleth.utilities.java.support.resolver.ResolverException; + +@Slf4j +public abstract class AbstractChainingMetadataProvider implements IGarbageCollectorProcessing, + IRefreshableMetadataProvider, IPvpAddableChainingMetadataProvider, + IDestroyableObject, IPvp2MetadataProvider, ClearableMetadataResolver { + + @Nonnull + @NonnullElements + private final List<MetadataResolver> internalResolvers; + private DateTime lastRefeshTimestamp; + private boolean lastRefeshSuccessful; + private static Object mutex = new Object(); + + /** + * Build a chaining metadata resolver that requires valid metadata. + * + */ + public AbstractChainingMetadataProvider() { + internalResolvers = Collections.synchronizedList(new ArrayList<>()); + + } + + /* + * (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 (final EaafConfigurationException e) { + log.error("Access to MOA-ID configuration FAILED.", e); + + } + } + + } + + @Override + public void fullyDestroy() { + internalDestroy(); + + } + + @Override + public synchronized boolean refreshMetadataProvider(final String entityId) { + try { + //if (resolveEntityDescriporForRefesh(entityId)) { + // return true; + // + //} + + // reload metadata provider + final String metadataUrl = getMetadataUrl(entityId); + if (StringUtils.isNotEmpty(metadataUrl)) { + final Map<String, MetadataResolver> actuallyLoadedResolver = + getAllActuallyLoadedResolvers(); + + // check if MetadataProvider is actually loaded + final MetadataResolver loadedResover = actuallyLoadedResolver.get(metadataUrl); + if (loadedResover instanceof RefreshableMetadataResolver) { + try { + ((RefreshableMetadataResolver) loadedResover).refresh(); + log.info("SAML2 metadata for service provider: {} is refreshed.", entityId); + return true; + + } catch (final ResolverException e) { + log.info("Can not refresh SAML2 metadata for entityId: {}. Reason: {}", entityId, e.getMessage()); + destroyMetadataResolver(loadedResover); + internalResolvers.remove(loadedResover); + + } + + } else { + // load new Metadata Provider + internalResolvers.add(createNewMetadataProvider(metadataUrl)); + + log.info("SAML2 metadata for service provider: {} is added.", entityId); + return true; + + } + + } else { + log.debug( + "Can not refresh SAML2 metadata: NO SAML2 metadata URL for SP with Id: {}", entityId); + } + + } catch (final IOException | EaafConfigurationException | CertificateException e) { + log.warn("Refresh SAML2 metadata for service provider: " + entityId + " FAILED.", e); + + } + + return false; + + } + + + @Override + public final MetadataFilter getMetadataFilter() { + log.warn("{} does NOT support {}", AbstractChainingMetadataProvider.class.getName(), + MetadataFilter.class.getName()); + return null; + } + + @Override + public final void setMetadataFilter(final MetadataFilter newFilter) { + log.warn("{} does NOT support {}", AbstractChainingMetadataProvider.class.getName(), + MetadataFilter.class.getName()); + throw new UnsupportedOperationException( + "Metadata filters are not supported on AbstractChainingMetadataProvider"); + } + + /* + * (non-Javadoc) + * + * @see at.gv.egovernment.moa.id.protocols.pvp2x.metadata.IEAAFMetadataProvider# + * getEntityDescriptor( java.lang.String) + */ + @Override + public final EntityDescriptor getEntityDescriptor(final String entityID) + throws ResolverException { + EntityDescriptor entityDesc = null; + try { + entityDesc = resolveEntityDescripor(entityID); + if (entityDesc == null) { + log.debug("Can not find PVP metadata for entityID: " + entityID + + " Start refreshing process ..."); + if (refreshMetadataProvider(entityID)) { + return resolveEntityDescripor(entityID); + } + } + + } catch (final ResolverException e) { + log.debug( + "Can not find PVP metadata for entityID: " + entityID + " Start refreshing process ..."); + if (refreshMetadataProvider(entityID)) { + return resolveEntityDescripor(entityID); + + } + + } + + return entityDesc; + } + + @Override + @Nullable + public final EntityDescriptor resolveSingle(@Nullable final CriteriaSet criteria) + throws ResolverException { + EntityDescriptor result = internalResolveSingle(criteria); + if (result == null && criteria != null) { + final EntityIdCriterion entityIdCriteria = criteria.get(EntityIdCriterion.class); + if (entityIdCriteria != null + && refreshMetadataProvider(entityIdCriteria.getEntityId())) { + log.debug("Can not find PVP metadata for entityID: {}. Metadata refreshing was done ... ", + entityIdCriteria.getEntityId()); + result = internalResolveSingle(criteria); + + } + } + + return result; + + } + + @Override + @Nonnull + public final Iterable<EntityDescriptor> resolve(@Nullable final CriteriaSet criteria) + throws ResolverException { + Iterable<EntityDescriptor> result = internalResolve(criteria); + if (criteria != null) { + final EntityIdCriterion entityIdCriteria = criteria.get(EntityIdCriterion.class); + if (!result.iterator().hasNext() && entityIdCriteria != null + && refreshMetadataProvider(entityIdCriteria.getEntityId())) { + log.debug("Can not find PVP metadata for entityID: {}. Metadata refreshing was done ... ", + entityIdCriteria.getEntityId()); + result = internalResolve(criteria); + } + } + + return result; + } + + @Override + public final void clear() throws ResolverException { + for (final MetadataResolver resolver : internalResolvers) { + if (resolver instanceof ClearableMetadataResolver) { + ((ClearableMetadataResolver) resolver).clear(); + } + } + } + + @Override + public final void clear(String entityID) throws ResolverException { + for (final MetadataResolver resolver : internalResolvers) { + if (resolver instanceof ClearableMetadataResolver) { + ((ClearableMetadataResolver) resolver).clear(entityID); + } + } + } + + @Override + public final void refresh() throws ResolverException { + this.lastRefeshSuccessful = false; + for (final MetadataResolver resolver : internalResolvers) { + if (resolver instanceof RefreshableMetadataResolver) { + ((RefreshableMetadataResolver) resolver).refresh(); + + } + } + + this.lastRefeshTimestamp = DateTime.now(); + this.lastRefeshSuccessful = true; + } + + @Override + @Nullable + public final DateTime getLastUpdate() { + DateTime ret = null; + for (final MetadataResolver resolver : internalResolvers) { + if (resolver instanceof RefreshableMetadataResolver) { + final DateTime lastUpdate = ((RefreshableMetadataResolver) resolver).getLastUpdate(); + if (ret == null || ret.isBefore(lastUpdate)) { + ret = lastUpdate; + } + } + } + + return ret; + } + + @Override + @Nullable + public final DateTime getLastRefresh() { + DateTime ret = null; + for (final MetadataResolver resolver : internalResolvers) { + if (resolver instanceof RefreshableMetadataResolver) { + final DateTime lastRefresh = ((RefreshableMetadataResolver) resolver).getLastRefresh(); + if (ret == null || ret.isBefore(lastRefresh)) { + ret = lastRefresh; + } + } + } + + return ret; + } + + @Override + public final DateTime getLastSuccessfulRefresh() { + return this.lastRefeshTimestamp; + + } + + @Override + public final Boolean wasLastRefreshSuccess() { + return this.lastRefeshSuccessful; + + } + + @Override + public final boolean isRequireValidMetadata() { + log.warn("Attempt to access unsupported requireValidMetadata property on ChainingMetadataResolver"); + return false; + } + + @Override + public final void setRequireValidMetadata(final boolean requireValidMetadata) { + throw new UnsupportedOperationException( + "Setting requireValidMetadata is not supported on chaining resolver"); + } + + @Override + public final String getId() { + return getMetadataProviderId(); + + } + + @Override + public final void addMetadataResolverIntoChain(MetadataResolver resolver) { + internalResolvers.add(resolver); + + } + + /** + * Get the URL to metadata for a specific entityID. + * + * @param entityId EntityId + * @return URL to metadata + * @throws EaafConfigurationException In case of an error + */ + protected abstract String getMetadataUrl(String entityId) throws EaafConfigurationException; + + /** + * Creates a new implementation specific SAML2 metadata provider. + * + * @param entityId EntityId + * @return MetadataResolver + * @throws EaafConfigurationException In case of an error + * @throws IOException In case of an error + * @throws CertificateException In case of an error + * @throws ConfigurationException In case of an error + */ + protected abstract MetadataResolver createNewMetadataProvider(String entityId) + throws EaafConfigurationException, IOException, CertificateException; + + /** + * Get a List of metadata URLs for all SAML2 SPs from configuration. + * + * @throws EaafConfigurationException In case of an error + */ + @Nonnull + protected abstract List<String> getAllMetadataUrlsFromConfiguration() + throws EaafConfigurationException; + + /** + * Get a Id for this metadata provider. + * + * @return + */ + @Nonnull + protected abstract String getMetadataProviderId(); + + protected final MetadataResolver getMetadataResolver() { + log.warn("{} does NOT support 'getMetadataResolver'", AbstractChainingMetadataProvider.class.getName()); + return null; + + } + + private Map<String, MetadataResolver> getAllActuallyLoadedResolvers() { + final Map<String, MetadataResolver> loadedproviders = + new HashMap<>(); + + // make a Map of all actually loaded HTTPMetadataProvider + for (final MetadataResolver resolver : internalResolvers) { + loadedproviders.put(((IdentifiedComponent) resolver).getId(), resolver); + + } + + return loadedproviders; + } + + private void addAndRemoveMetadataProvider() throws EaafConfigurationException { + log.info("EAAF chaining metadata resolver starting internal managment task .... "); + + // get all actually loaded metadata providers + final Map<String, MetadataResolver> loadedproviders = getAllActuallyLoadedResolvers(); + + /* + * 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 + final List<String> allMetadataUrls = getAllMetadataUrlsFromConfiguration(); + + final Iterator<String> metadataUrlInterator = allMetadataUrls.iterator(); + while (metadataUrlInterator.hasNext()) { + final String metadataurl = metadataUrlInterator.next(); + try { + if (StringUtils.isNotEmpty(metadataurl) + && loadedproviders.containsKey(metadataurl)) { + // SAML2 SP is actually loaded, to nothing + loadedproviders.remove(metadataurl); + + } + } catch (final 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 + final Collection<MetadataResolver> notusedproviders = loadedproviders.values(); + for (final MetadataResolver resolver : notusedproviders) { + log.info("Remove not used MetadataProvider with MetadataURL " + resolver.getId()); + destroyMetadataResolver(resolver); + internalResolvers.remove(resolver); + + } + + } + + private EntityDescriptor resolveEntityDescripor(String entityId) throws ResolverException { + final CriteriaSet criteria = new CriteriaSet(); + criteria.add(new EntityIdCriterion(entityId)); + return internalResolveSingle(criteria); + + } + + private void destroyMetadataResolver(MetadataResolver resolver) { + if (resolver instanceof AbstractMetadataResolver) { + final AbstractMetadataResolver httpprovider = (AbstractMetadataResolver) resolver; + log.debug("Destroy metadata resolver with id: {}", httpprovider.getId()); + httpprovider.destroy(); + + } else { + log.warn("Metadata resolver: {} can not be destroyed. Reason: unsupported type: {}", + resolver.getId(), resolver.getClass().getName()); + + } + } + + /** + * Close metadata provider and remove all loaded metadata. + * + */ + private void internalDestroy() { + log.info("Destroying chained metadata resolvers ..."); + + for (final MetadataResolver resolver : internalResolvers) { + destroyMetadataResolver(resolver); + } + + internalResolvers.clear(); + + } + + @Nullable + private EntityDescriptor internalResolveSingle(@Nullable final CriteriaSet criteria) + throws ResolverException { + for (final MetadataResolver resolver : internalResolvers) { + try { + final EntityDescriptor descriptors = resolver.resolveSingle(criteria); + if (descriptors != null) { + return descriptors; + } + + } catch (final ResolverException e) { + continue; + + } + } + + return null; + + } + + @Nonnull + private Iterable<EntityDescriptor> internalResolve(@Nullable final CriteriaSet criteria) + throws ResolverException { + for (final MetadataResolver resolver : internalResolvers) { + try { + final Iterable<EntityDescriptor> descriptors = resolver.resolve(criteria); + if (descriptors != null && descriptors.iterator().hasNext()) { + return descriptors; + + } + + } catch (final ResolverException e) { + continue; + + } + } + + return Collections.emptyList(); + } + } |