From e7610325ee2f1d1f4e97e1e7a9b212e692836b5a Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Tue, 4 Feb 2020 17:37:34 +0100 Subject: first stable version that uses OpenSAML 3.x --- .../impl/metadata/PvpMetadataResolverFactory.java | 293 +++++++++++---------- 1 file changed, 150 insertions(+), 143 deletions(-) (limited to 'eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/PvpMetadataResolverFactory.java') diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/PvpMetadataResolverFactory.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/PvpMetadataResolverFactory.java index f548bc7b..0b505e56 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/PvpMetadataResolverFactory.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/metadata/PvpMetadataResolverFactory.java @@ -8,23 +8,31 @@ import javax.annotation.Nullable; import javax.annotation.PostConstruct; import javax.net.ssl.SSLHandshakeException; +import at.gv.egiz.components.spring.api.IDestroyableObject; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.impl.utils.FileUtils; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; +import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2MetadataException; +import at.gv.egiz.eaaf.modules.pvp2.exception.SchemaValidationException; +import at.gv.egiz.eaaf.modules.pvp2.exception.SignatureValidationException; +import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.OpenSaml3ResourceAdapter; + import org.apache.http.client.HttpClient; import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; import org.opensaml.saml.metadata.resolver.ExtendedRefreshableMetadataResolver; import org.opensaml.saml.metadata.resolver.filter.MetadataFilter; +import org.opensaml.saml.metadata.resolver.impl.AbstractReloadingMetadataResolver; import org.opensaml.saml.metadata.resolver.impl.HTTPMetadataResolver; import org.opensaml.saml.metadata.resolver.impl.ResourceBackedMetadataResolver; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ResourceLoader; -import at.gv.egiz.components.spring.api.IDestroyableObject; -import at.gv.egiz.eaaf.core.api.idp.IConfiguration; -import at.gv.egiz.eaaf.core.impl.utils.FileUtils; -import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; -import at.gv.egiz.eaaf.modules.pvp2.exception.SchemaValidationException; -import at.gv.egiz.eaaf.modules.pvp2.exception.SignatureValidationException; -import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.OpenSaml3ResourceAdapter; +import com.google.common.base.Predicates; +import com.google.common.base.Throwables; +import com.google.common.collect.FluentIterable; import lombok.extern.slf4j.Slf4j; +import net.shibboleth.utilities.java.support.component.ComponentInitializationException; +import net.shibboleth.utilities.java.support.resolver.ResolverException; import net.shibboleth.utilities.java.support.resource.Resource; import net.shibboleth.utilities.java.support.xml.ParserPool; @@ -34,13 +42,18 @@ public class PvpMetadataResolverFactory implements IDestroyableObject { private static final String URI_PREFIX_HTTP = "http:"; private static final String URI_PREFIX_HTTPS = "https:"; + private static final String NOT_SUCCESS = "Maybe metadata was expired"; + private Timer timer = null; - - @Autowired private IConfiguration authConfig; - @Autowired private ResourceLoader resourceLoader; - + + @Autowired + private IConfiguration authConfig; + @Autowired + private ResourceLoader resourceLoader; + /** - * Create a single SAML2 metadata provider by using the default OpenSAML3 parser-pool. + * Create a single SAML2 metadata provider by using the default OpenSAML3 + * parser-pool. * * @param metadataLocation where the metadata should be loaded, but never null. * If the location starts with http(s):, than a http @@ -54,17 +67,18 @@ public class PvpMetadataResolverFactory implements IDestroyableObject { * * @return SAML2 Metadata Provider, or null if the metadata provider can not * initialized + * @throws Pvp2MetadataException In case of an initialization error */ - @Nullable + @Nullable public IPvp2MetadataProvider createMetadataProvider(@Nonnull final String metadataLocation, @Nullable final MetadataFilter filter, @Nonnull final String idForLogging, - @Nullable final HttpClient httpClient) { - return createMetadataProvider(metadataLocation, filter, idForLogging, - XMLObjectProviderRegistrySupport.getParserPool(), - httpClient); - + @Nullable final HttpClient httpClient) throws Pvp2MetadataException { + return createMetadataProvider(metadataLocation, filter, idForLogging, + XMLObjectProviderRegistrySupport.getParserPool(), + httpClient); + } - + /** * Create a single SAML2 metadata provider. * @@ -80,57 +94,74 @@ public class PvpMetadataResolverFactory implements IDestroyableObject { * * @return SAML2 Metadata Provider, or null if the metadata provider can not * initialized + * @throws Pvp2MetadataException In case of an initialization error */ - @Nullable + @Nullable public IPvp2MetadataProvider createMetadataProvider(@Nonnull final String metadataLocation, @Nullable final MetadataFilter filter, @Nonnull final String idForLogging, - @Nullable final ParserPool pool, @Nullable final HttpClient httpClient) { - + @Nullable final ParserPool pool, @Nullable final HttpClient httpClient) throws Pvp2MetadataException { + ExtendedRefreshableMetadataResolver internalProvider = null; - - if (metadataLocation.startsWith(URI_PREFIX_HTTP) - || metadataLocation.startsWith(URI_PREFIX_HTTPS)) { - if (httpClient != null) { - internalProvider = createNewHttpMetaDataProvider(metadataLocation, filter, idForLogging, timer, pool, - httpClient); + + try { + if (metadataLocation.startsWith(URI_PREFIX_HTTP) + || metadataLocation.startsWith(URI_PREFIX_HTTPS)) { + internalProvider = createNewHttpMetaDataProvider(metadataLocation, filter, + idForLogging, timer, pool, httpClient); + } else { - log.warn("Can not load http(s) based SAML2 metadata without a HTTP client"); - + final String absoluteMetadataLocation = + FileUtils.makeAbsoluteUrl(metadataLocation, authConfig.getConfigurationRootDirectory()); + final org.springframework.core.io.Resource resource = + resourceLoader.getResource(absoluteMetadataLocation); + + if (resource.exists()) { + internalProvider = createNewFileSystemMetaDataProvider( + new OpenSaml3ResourceAdapter(resource), + filter, idForLogging, timer, + pool); + + } else { + log.warn( + "SAML2 metadata file: {} not found or not exist", absoluteMetadataLocation); + throw new Pvp2MetadataException("internal.pvp.05", + new Object[] { absoluteMetadataLocation, "File NOT found or exist." }); + + } } - } else { - String absoluteMetadataLocation; - try { - absoluteMetadataLocation = - FileUtils.makeAbsoluteUrl(metadataLocation, authConfig.getConfigurationRootDirectory()); + } catch (final ComponentInitializationException e) { + log.warn("Failed to load Metadata file for {} [ {} ]", + idForLogging, e.getMessage()); + checkResolverInitializationError(e, metadataLocation); - org.springframework.core.io.Resource resource = resourceLoader.getResource(absoluteMetadataLocation); - if (resource.exists()) { - internalProvider = createNewFileSystemMetaDataProvider( - new OpenSaml3ResourceAdapter(resource), - filter, idForLogging, timer, - pool); - } else { - log.warn( - "SAML2 metadata file: " + absoluteMetadataLocation + " not found or not exist"); - - } + } catch (final Exception e) { + throw new Pvp2MetadataException("internal.pvp.09", new Object[] { metadataLocation, e.getMessage() }); + } - } catch (final IOException e) { - log.warn("SAML2 metadata URL is invalid: " + metadataLocation, e); + if (!internalProvider.wasLastRefreshSuccess()) { + log.info("Metadata loading from source: {} failed. {}", metadataLocation, NOT_SUCCESS); + throw new Pvp2MetadataException("internal.pvp.09", new Object[] { metadataLocation, NOT_SUCCESS }); - } } - if (internalProvider != null) { - return new PvpMetadataResolverAdapter(internalProvider); - - } else { - log.warn("SAML2 metadata has an unsupported metadata location prefix: " + metadataLocation); - return null; - + return new PvpMetadataResolverAdapter(internalProvider); + + } + + @Override + public void fullyDestroy() { + if (timer != null) { + log.info("Stopping timer-thread for PVP metadata resolver ... "); + timer.cancel(); } + } + + @PostConstruct + private void initialize() { + log.info("Initializing timer-thread for PVP metadata resolver ... "); + timer = new Timer("PVP metadata-resolver refresh"); } @@ -142,55 +173,26 @@ public class PvpMetadataResolverFactory implements IDestroyableObject { * @param idForLogging Id, which is used for Logging * @param timer {@link Timer} which is used to schedule metadata refresh * operations - * @param pool + * @param pool SAML2 parser pool that should be used * * @return SAML2 Metadata Provider - * @throws IOException + * @throws IOException In case of a metadata resource error + * @throws ComponentInitializationException In case of a metadata resolver + * initialization error */ private ExtendedRefreshableMetadataResolver createNewFileSystemMetaDataProvider(final Resource metadataFile, final MetadataFilter filter, final String idForLogging, final Timer timer, - final ParserPool pool) throws IOException { + final ParserPool pool) throws IOException, ComponentInitializationException { ResourceBackedMetadataResolver fileSystemResolver = null; - try { - //fileSystemResolver = new FilesystemMetadataResolver(timer, metadataFile); - - fileSystemResolver = new ResourceBackedMetadataResolver(timer, metadataFile); - - if (pool != null) { - fileSystemResolver.setParserPool(pool); - - } else { - fileSystemResolver.setParserPool( - XMLObjectProviderRegistrySupport.getParserPool()); - - } - fileSystemResolver.setRequireValidMetadata(true); - fileSystemResolver.setMinRefreshDelay(1000 * 60 * 15); // 15 minutes - fileSystemResolver.setMaxRefreshDelay(1000 * 60 * 60 * 24); // 24 hours - - fileSystemResolver.setMetadataFilter(filter); - fileSystemResolver.initialize(); - fileSystemResolver.setId(metadataFile.getURI().toASCIIString()); - - fileSystemResolver.setRequireValidMetadata(true); - - return fileSystemResolver; - - } catch (final Exception e) { - log.warn("Failed to load Metadata file for " + idForLogging + "[ " + "File: " - + metadataFile.getURI().toASCIIString() + " Msg: " + e.getMessage() + " ]", e); - - log.warn("Can not initialize SAML2 metadata provider from filesystem: " - + metadataFile.getURI().toASCIIString() + " Reason: " + e.getMessage(), e); + fileSystemResolver = new ResourceBackedMetadataResolver(timer, metadataFile); + injectMetadataResolverConfiguration(fileSystemResolver, filter, pool); + fileSystemResolver.setId(metadataFile.getURI().toASCIIString()); + fileSystemResolver.initialize(); - if (fileSystemResolver != null) { - fileSystemResolver.destroy(); + log.trace("Set-up metadata-resolver with ID: {} as: {}", + idForLogging, fileSystemResolver.getClass().getSimpleName()); - } - - } - - return null; + return fileSystemResolver; } @@ -202,70 +204,75 @@ public class PvpMetadataResolverFactory implements IDestroyableObject { * @param idForLogging Id, which is used for Logging * @param timer {@link Timer} which is used to schedule metadata refresh * operations - * @param pool - * + * @param pool SAML2 parser pool that should be used * @return SAML2 Metadata Provider + * @throws ComponentInitializationException In case of a metadata resolver + * initialization error + * @throws ResolverException In case of an internal OpenSAML + * resolver error */ private ExtendedRefreshableMetadataResolver createNewHttpMetaDataProvider(final String metadataUrl, final MetadataFilter filter, final String idForLogging, final Timer timer, - final ParserPool pool, final HttpClient httpClient) { + final ParserPool pool, final HttpClient httpClient) throws ComponentInitializationException, + ResolverException { HTTPMetadataResolver httpMetadataResolver = null; - try { - httpMetadataResolver = new HTTPMetadataResolver(timer, httpClient, metadataUrl); - httpMetadataResolver.setParserPool(pool); - httpMetadataResolver.setRequireValidMetadata(true); - httpMetadataResolver.setMinRefreshDelay(1000 * 60 * 15); // 15 minutes - httpMetadataResolver.setMaxRefreshDelay(1000 * 60 * 60 * 24); // 24 hours - // httpProvider.setRefreshDelayFactor(0.1F); + httpMetadataResolver = new HTTPMetadataResolver(timer, httpClient, metadataUrl); + injectMetadataResolverConfiguration(httpMetadataResolver, filter, pool); + httpMetadataResolver.setId(metadataUrl); + httpMetadataResolver.initialize(); - httpMetadataResolver.setMetadataFilter(filter); - httpMetadataResolver.setId(metadataUrl); - httpMetadataResolver.initialize(); + log.trace("Set-up metadata-resolver with ID: {} as: {}", + idForLogging, httpMetadataResolver.getClass().getSimpleName()); - httpMetadataResolver.setRequireValidMetadata(true); + return httpMetadataResolver; - return httpMetadataResolver; + } - } catch (final Throwable e) { - if (e.getCause() != null && e.getCause().getCause() instanceof SSLHandshakeException) { - log.warn("SSL-Server certificate for metadata " + metadataUrl + " not trusted.", e); + private void injectMetadataResolverConfiguration(AbstractReloadingMetadataResolver resolver, + final MetadataFilter filter, final ParserPool pool) { + if (pool != null) { + resolver.setParserPool(pool); - } - if (e.getCause() != null && e.getCause().getCause() instanceof SignatureValidationException) { - log.warn("Signature verification for metadata" + metadataUrl + " FAILED.", e); + } else { + resolver.setParserPool( + XMLObjectProviderRegistrySupport.getParserPool()); - } - if (e.getCause() != null && e.getCause().getCause() instanceof SchemaValidationException) { - log.warn("Schema validation for metadata " + metadataUrl + " FAILED.", e); - } + } + + resolver.setRequireValidMetadata(true); + resolver.setMinRefreshDelay(1000 * 60 * 15); // 15 minutes + resolver.setMaxRefreshDelay(1000 * 60 * 60 * 24); // 24 hours + resolver.setMetadataFilter(filter); + + } - log.warn("Failed to load Metadata file for " + idForLogging + "[ " + e.getMessage() + " ]", + private void checkResolverInitializationError(ComponentInitializationException e, String metadataLocation) + throws Pvp2MetadataException { + if (FluentIterable.from(Throwables.getCausalChain(e)).filter( + Predicates.instanceOf(SSLHandshakeException.class)).first().isPresent()) { + log.info("SSL-Server certificate for metadata: {} not trusted.", metadataLocation, null, e); + throw new Pvp2MetadataException("internal.pvp.06", new Object[] { metadataLocation, e.getMessage() }, e); - if (httpMetadataResolver != null) { - log.debug("Destroy failed Metadata provider"); - httpMetadataResolver.destroy(); + } else if (FluentIterable.from(Throwables.getCausalChain(e)).filter( + Predicates.instanceOf(SignatureValidationException.class)).first().isPresent()) { + log.info("Signature verification for metadata: {} FAILED.", metadataLocation, null, e); + throw new Pvp2MetadataException("internal.pvp.07", new Object[] { metadataLocation, e.getMessage() }, + e); - } + } else if (FluentIterable.from(Throwables.getCausalChain(e)).filter( + Predicates.instanceOf(SchemaValidationException.class)).first().isPresent()) { + log.info("Schema validation for metadata: {} FAILED.", metadataLocation, null, e); + throw new Pvp2MetadataException("internal.pvp.08", new Object[] { metadataLocation, e.getMessage() }, + e); + + } else { + log.info("Generic initialization error for metadata: {}", metadataLocation, null, e); + throw new Pvp2MetadataException("internal.pvp.09", new Object[] { metadataLocation, e.getMessage() }, + e); } - return null; } - @Override - public void fullyDestroy() { - if (timer != null) { - log.info("Stopping timer-thread for PVP metadata resolver ... "); - timer.cancel(); - } - } - - @PostConstruct - private void initialize() { - log.info("Initializing timer-thread for PVP metadata resolver ... "); - timer = new Timer("PVP metadata-resolver refresh"); - - } - } -- cgit v1.2.3