org.opensaml.saml2.metadata.provider.HTTPMetadataProvider Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of opensaml Show documentation
Show all versions of opensaml Show documentation
The OpenSAML-J library provides tools to support developers working with the Security Assertion Markup Language
(SAML).
/*
* Licensed to the University Corporation for Advanced Internet Development,
* Inc. (UCAID) under one or more contributor license agreements. See the
* NOTICE file distributed with this work for additional information regarding
* copyright ownership. The UCAID licenses this file to You under the Apache
* License, Version 2.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.opensaml.saml2.metadata.provider;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Timer;
import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A metadata provider that pulls metadata using an HTTP GET. Metadata is cached until one of these criteria is met:
*
* - The smallest cacheDuration within the metadata is exceeded
* - The earliest validUntil time within the metadata is exceeded
* - The maximum cache duration is exceeded
*
*
* Metadata is filtered prior to determining the cache expiration data. This allows a filter to remove XMLObjects that
* may effect the cache duration but for which the user of this provider does not care about.
*
* It is the responsibility of the caller to re-initialize, via {@link #initialize()}, if any properties of this
* provider are changed.
*/
public class HTTPMetadataProvider extends AbstractReloadingMetadataProvider {
/** Class logger. */
private final Logger log = LoggerFactory.getLogger(HTTPMetadataProvider.class);
/** HTTP Client used to pull the metadata. */
private HttpClient httpClient;
/** URL to the Metadata. */
private URI metadataURI;
/** The ETag provided when the currently cached metadata was fetched. */
private String cachedMetadataETag;
/** The Last-Modified information provided when the currently cached metadata was fetched. */
private String cachedMetadataLastModified;
/** URL scope that requires authentication. */
private AuthScope authScope;
/**
* Constructor.
*
* @param metadataURL the URL to fetch the metadata
* @param requestTimeout the time, in milliseconds, to wait for the metadata server to respond
*
* @throws MetadataProviderException thrown if the URL is not a valid URL or the metadata can not be retrieved from
* the URL
*/
@Deprecated
public HTTPMetadataProvider(String metadataURL, int requestTimeout) throws MetadataProviderException {
super();
try {
metadataURI = new URI(metadataURL);
} catch (URISyntaxException e) {
throw new MetadataProviderException("Illegal URL syntax", e);
}
HttpClientParams clientParams = new HttpClientParams();
clientParams.setSoTimeout(requestTimeout);
httpClient = new HttpClient(clientParams);
httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(requestTimeout);
authScope = new AuthScope(metadataURI.getHost(), metadataURI.getPort());
}
/**
* Constructor.
*
* @param client HTTP client used to pull in remote metadata
* @param backgroundTaskTimer timer used to schedule background metadata refresh tasks
* @param metadataURL URL to the remove remote metadata
*
* @throws MetadataProviderException thrown if the HTTP client is null or the metadata URL provided is invalid
*/
public HTTPMetadataProvider(Timer backgroundTaskTimer, HttpClient client, String metadataURL)
throws MetadataProviderException {
super(backgroundTaskTimer);
if (client == null) {
throw new MetadataProviderException("HTTP client may not be null");
}
httpClient = client;
try {
metadataURI = new URI(metadataURL);
} catch (URISyntaxException e) {
throw new MetadataProviderException("Illegal URL syntax", e);
}
authScope = new AuthScope(metadataURI.getHost(), metadataURI.getPort());
}
/**
* Gets the URL to fetch the metadata.
*
* @return the URL to fetch the metadata
*/
public String getMetadataURI() {
return metadataURI.toASCIIString();
}
/**
* Sets the username and password used to access the metadata URL. To disable BASIC authentication set the username
* and password to null;
*
* @param username the username
* @param password the password
*/
public void setBasicCredentials(String username, String password) {
if (username == null && password == null) {
httpClient.getState().setCredentials(null, null);
} else {
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password);
httpClient.getState().setCredentials(authScope, credentials);
}
}
/**
* Gets the length of time in milliseconds to wait for the server to respond.
*
* @return length of time in milliseconds to wait for the server to respond
*/
public int getRequestTimeout() {
return httpClient.getParams().getSoTimeout();
}
/**
* Sets the socket factory used to create sockets to the HTTP server.
*
* @see HTTPClient SSL guide
*
* @param newSocketFactory the socket factory used to produce sockets used to connect to the server
*
* @deprecated set this information on HTTP client used by provider
*/
public void setSocketFactory(ProtocolSocketFactory newSocketFactory) {
log.debug("Using the custom socket factory {} to connect to the HTTP server", newSocketFactory.getClass()
.getName());
Protocol protocol = new Protocol(metadataURI.getScheme(), newSocketFactory, metadataURI.getPort());
httpClient.getHostConfiguration().setHost(metadataURI.getHost(), metadataURI.getPort(), protocol);
}
/**
* Gets the maximum amount of time, in seconds, metadata will be cached for.
*
* @return maximum amount of time, in seconds, metadata will be cached for
*
* @deprecated use {@link #getMaxRefreshDelay()} instead
*/
public int getMaxCacheDuration() {
return (int) getMaxRefreshDelay();
}
/**
* Sets the maximum amount of time, in seconds, metadata will be cached for.
*
* @param newDuration maximum amount of time, in seconds, metadata will be cached for
*
* @deprecated use {@link #setMaxRefreshDelay(long)} instead
*/
public void setMaxCacheDuration(int newDuration) {
setMaxRefreshDelay(newDuration * 1000);
}
/**
* Gets whether cached metadata should be discarded if it expires and can not be refreshed.
*
* @return whether cached metadata should be discarded if it expires and can not be refreshed.
*
* @deprecated use {@link #requireValidMetadata()} instead
*/
public boolean maintainExpiredMetadata() {
return !requireValidMetadata();
}
/**
* Sets whether cached metadata should be discarded if it expires and can not be refreshed.
*
* @param maintain whether cached metadata should be discarded if it expires and can not be refreshed.
*
* @deprecated use {@link #setRequireValidMetadata(boolean)} instead
*/
public void setMaintainExpiredMetadata(boolean maintain) {
setRequireValidMetadata(!maintain);
}
/** {@inheritDoc} */
public synchronized void destroy() {
httpClient = null;
metadataURI = null;
cachedMetadataETag = null;
cachedMetadataLastModified = null;
authScope = null;
super.destroy();
}
/** {@inheritDoc} */
protected String getMetadataIdentifier() {
return metadataURI.toString();
}
/**
* Gets the metadata document from the remote server.
*
* @return the metadata from remote server, or null if the metadata document has not changed since the last
* retrieval
*
* @throws MetadataProviderException thrown if there is a problem retrieving the metadata from the remote server
*/
protected byte[] fetchMetadata() throws MetadataProviderException {
GetMethod getMethod = buildGetMethod();
try {
log.debug("Attempting to fetch metadata document from '{}'", metadataURI);
httpClient.executeMethod(getMethod);
int httpStatus = getMethod.getStatusCode();
if (httpStatus == HttpStatus.SC_NOT_MODIFIED) {
log.debug("Metadata document from '{}' has not changed since last retrieval", getMetadataURI());
return null;
}
if (getMethod.getStatusCode() != HttpStatus.SC_OK) {
String errMsg = "Non-ok status code " + getMethod.getStatusCode()
+ " returned from remote metadata source " + metadataURI;
log.error(errMsg);
throw new MetadataProviderException(errMsg);
}
processConditionalRetrievalHeaders(getMethod);
byte[] rawMetadata = getMetadataBytesFromResponse(getMethod);
log.debug("Successfully fetched {}bytes of metadata from {}", rawMetadata.length, getMetadataURI());
return rawMetadata;
} catch (IOException e) {
String errMsg = "Error retrieving metadata from " + metadataURI;
log.error(errMsg, e);
throw new MetadataProviderException(errMsg, e);
}finally{
getMethod.releaseConnection();
}
}
/**
* Builds the HTTP GET method used to fetch the metadata. The returned method advertises support for GZIP and
* deflate compression, enables conditional GETs if the cached metadata came with either an ETag or Last-Modified
* information, and sets up basic authentication if such is configured.
*
* @return the constructed GET method
*/
protected GetMethod buildGetMethod() {
GetMethod getMethod = new GetMethod(getMetadataURI());
getMethod.addRequestHeader("Connection", "close");
getMethod.setRequestHeader("Accept-Encoding", "gzip,deflate");
if (cachedMetadataETag != null) {
getMethod.setRequestHeader("If-None-Match", cachedMetadataETag);
}
if (cachedMetadataLastModified != null) {
getMethod.setRequestHeader("If-Modified-Since", cachedMetadataLastModified);
}
if (httpClient.getState().getCredentials(authScope) != null) {
log.debug("Using BASIC authentication when retrieving metadata from '{}", metadataURI);
getMethod.setDoAuthentication(true);
}
return getMethod;
}
/**
* Records the ETag and Last-Modified headers, from the response, if they are present.
*
* @param getMethod GetMethod containing a valid HTTP response
*/
protected void processConditionalRetrievalHeaders(GetMethod getMethod) {
Header httpHeader = getMethod.getResponseHeader("ETag");
if (httpHeader != null) {
cachedMetadataETag = httpHeader.getValue();
}
httpHeader = getMethod.getResponseHeader("Last-Modified");
if (httpHeader != null) {
cachedMetadataLastModified = httpHeader.getValue();
}
}
/**
* Extracts the raw metadata bytes from the response taking in to account possible deflate and GZip compression.
*
* @param getMethod GetMethod containing a valid HTTP response
*
* @return the raw metadata bytes
*
* @throws MetadataProviderException thrown if there is a problem getting the raw metadata bytes from the response
*/
protected byte[] getMetadataBytesFromResponse(GetMethod getMethod) throws MetadataProviderException {
log.debug("Attempting to extract metadata from response to request for metadata from '{}'", getMetadataURI());
try {
InputStream ins = getMethod.getResponseBodyAsStream();
Header httpHeader = getMethod.getResponseHeader("Content-Encoding");
if (httpHeader != null) {
String contentEncoding = httpHeader.getValue();
if ("deflate".equalsIgnoreCase(contentEncoding)) {
log.debug("Metadata document from '{}' was deflate compressed, decompressing it", metadataURI);
ins = new InflaterInputStream(ins);
}
if ("gzip".equalsIgnoreCase(contentEncoding)) {
log.debug("Metadata document from '{}' was GZip compressed, decompressing it", metadataURI);
ins = new GZIPInputStream(ins);
}
}
return inputstreamToByteArray(ins);
} catch (IOException e) {
log.error("Unable to read response", e);
throw new MetadataProviderException("Unable to read response", e);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy