/* * 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: * 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. */ package at.gv.egiz.eaaf.modules.pvp2.impl.metadata; import java.io.File; import java.net.MalformedURLException; import java.util.Timer; import javax.net.ssl.SSLHandshakeException; 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.exception.SchemaValidationException; import at.gv.egiz.eaaf.modules.pvp2.exception.SignatureValidationException; import org.apache.http.client.HttpClient; import org.opensaml.saml.metadata.resolver.MetadataResolver; import org.opensaml.saml.metadata.resolver.filter.MetadataFilter; import org.opensaml.saml.metadata.resolver.impl.FilesystemMetadataResolver; import org.opensaml.saml.metadata.resolver.impl.HTTPMetadataResolver; import org.springframework.beans.factory.annotation.Autowired; import lombok.extern.slf4j.Slf4j; import net.shibboleth.utilities.java.support.xml.ParserPool; /** * Simple SAML2 metadata provider. * * @author tlenz * */ @Slf4j public abstract class SimpleMetadataResolver implements MetadataResolver { private static final String URI_PREFIX_HTTP = "http:"; private static final String URI_PREFIX_HTTPS = "https:"; private static final String URI_PREFIX_FILE = "file:"; @Autowired protected IConfiguration authConfig; @Override public final boolean isRequireValidMetadata() { return true; } @Override public final void setRequireValidMetadata(final boolean requireValidMetadata) { log.warn("EAAF {} requires always valid metadata. Setting will be ignored", SimpleMetadataResolver.class.getSimpleName()); } /** * Create a single SAML2 metadata provider. * * @param metadataLocation where the metadata should be loaded, but never null. * If the location starts with http(s):, than a http * based metadata provider is used. If the location * starts with file:, than a filesystem based metadata * provider is used * @param filter Filters, which should be used to validate the * metadata * @param idForLogging Id, which is used for Logging * @param timer {@link Timer} which is used to schedule metadata * refresh operations * @param httpClient Apache commons 3.x http client * * @return SAML2 Metadata Provider, or null if the metadata provider can not * initialized */ protected MetadataResolver createNewSimpleMetadataProvider(final String metadataLocation, final MetadataFilter filter, final String idForLogging, final Timer timer, final ParserPool pool, final HttpClient httpClient) { if (metadataLocation.startsWith(URI_PREFIX_HTTP) || metadataLocation.startsWith(URI_PREFIX_HTTPS)) { if (httpClient != null) { return createNewHttpMetaDataProvider(metadataLocation, filter, idForLogging, timer, pool, httpClient); } else { log.warn("Can not load http(s) based SAML2 metadata without a HTTP client"); return null; } } else { String absoluteMetadataLocation; try { absoluteMetadataLocation = FileUtils.makeAbsoluteUrl(metadataLocation, authConfig.getConfigurationRootDirectory()); if (absoluteMetadataLocation.startsWith(URI_PREFIX_FILE)) { final File metadataFile = new File(absoluteMetadataLocation); if (metadataFile.exists()) { return createNewFileSystemMetaDataProvider(metadataFile, filter, idForLogging, timer, pool); } else { log.warn( "SAML2 metadata file: " + absoluteMetadataLocation + " not found or not exist"); return null; } } } catch (final MalformedURLException e) { log.warn("SAML2 metadata URL is invalid: " + metadataLocation, e); } } log.warn("SAML2 metadata has an unsupported metadata location prefix: " + metadataLocation); return null; } /** * Create a single SAML2 filesystem based metadata provider. * * @param metadataFile File, where the metadata should be loaded * @param filter Filters, which should be used to validate the metadata * @param idForLogging Id, which is used for Logging * @param timer {@link Timer} which is used to schedule metadata refresh * operations * @param pool * * @return SAML2 Metadata Provider */ private MetadataResolver createNewFileSystemMetaDataProvider(final File metadataFile, final MetadataFilter filter, final String idForLogging, final Timer timer, final ParserPool pool) { FilesystemMetadataResolver fileSystemResolver = null; try { fileSystemResolver = new FilesystemMetadataResolver(timer, metadataFile); fileSystemResolver.setParserPool(pool); 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.getAbsolutePath()); fileSystemResolver.setRequireValidMetadata(true); return fileSystemResolver; } catch (final Exception e) { log.warn("Failed to load Metadata file for " + idForLogging + "[ " + "File: " + metadataFile.getAbsolutePath() + " Msg: " + e.getMessage() + " ]", e); log.warn("Can not initialize SAML2 metadata provider from filesystem: " + metadataFile.getAbsolutePath() + " Reason: " + e.getMessage(), e); if (fileSystemResolver != null) { fileSystemResolver.destroy(); } } return null; } /** * Create a single SAML2 HTTP metadata provider. * * @param metadataUrl URL, where the metadata should be loaded * @param filter Filters, which should be used to validate the metadata * @param idForLogging Id, which is used for Logging * @param timer {@link Timer} which is used to schedule metadata refresh * operations * @param pool * * @return SAML2 Metadata Provider */ private MetadataResolver createNewHttpMetaDataProvider(final String metadataUrl, final MetadataFilter filter, final String idForLogging, final Timer timer, final ParserPool pool, final HttpClient httpClient) { 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.setMetadataFilter(filter); httpMetadataResolver.setId(metadataUrl); httpMetadataResolver.initialize(); httpMetadataResolver.setRequireValidMetadata(true); 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); } if (e.getCause() != null && e.getCause().getCause() instanceof SignatureValidationException) { log.warn("Signature verification for metadata" + metadataUrl + " FAILED.", e); } if (e.getCause() != null && e.getCause().getCause() instanceof SchemaValidationException) { log.warn("Schema validation for metadata " + metadataUrl + " FAILED.", e); } log.warn("Failed to load Metadata file for " + idForLogging + "[ " + e.getMessage() + " ]", e); if (httpMetadataResolver != null) { log.debug("Destroy failed Metadata provider"); httpMetadataResolver.destroy(); } } return null; } }