blob: c70670bbfd3f1aeb3005e0b806109a43477325e5 [file] [log] [blame]
/*
* Copyright (C) 2010-2011 The University of Manchester
*
* See the file "LICENSE" for license terms.
*/
package org.taverna.server.master.common;
import static javax.ws.rs.core.UriBuilder.fromUri;
import static org.apache.commons.logging.LogFactory.getLog;
import static org.taverna.server.master.common.Namespaces.XLINK;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Map;
import javax.annotation.PreDestroy;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriBuilderException;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
import org.apache.commons.logging.Log;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.PortMapper;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.SuppressWarnings;
/**
* A class that makes it simpler to work with an element with a {@link URI} in
* an <tt>href</tt> attribute. Done with JAXB.
*
* @author Donal Fellows
*/
@XmlType(name = "Location")
public class Uri {
static Log log = getLog("Taverna.Server.UriRewriter");
private static final String SECURE_SCHEME = "https";
/**
* This type is characterised by an attribute that is the reference to some
* other element.
*/
@XmlAttribute(name = "href", namespace = XLINK)
@XmlSchemaType(name = "anyURI")
public URI ref;
/** Make a reference that points nowhere. */
public Uri() {
}
/**
* Make a reference to the given location.
*
* @param ref
* Where to point to.
*/
public Uri(@NonNull URI ref) {
this.ref = secure(ref);
}
/**
* Make a reference from the factory with the given parameters.
*
* @param ub
* The configured factory.
* @param strings
* The parameters to the factory.
*/
public Uri(@NonNull UriBuilder ub, String... strings) {
ref = secure(ub).build((Object[]) strings);
}
/**
* Make a reference from the factory with the given parameters.
*
* @param ui
* The factory factory.
* @param path
* The path to configure the factory with.
* @param strings
* The parameters to the factory.
*/
public Uri(@NonNull UriInfo ui, @NonNull String path, String... strings) {
this(ui, true, path, strings);
}
/**
* Make a reference from the factory with the given parameters.
*
* @param ui
* The factory factory.
* @param secure
* Whether the URI should be required to use HTTPS.
* @param path
* The path to configure the factory with.
* @param strings
* The parameters to the factory.
*/
public Uri(@NonNull UriInfo ui, boolean secure, @NonNull String path,
String... strings) {
UriBuilder ub = ui.getAbsolutePathBuilder();
if (secure) {
ub = secure(ub);
}
ref = ub.path(path).build((Object[]) strings);
}
public static UriBuilder secure(UriBuilder ub) {
return Rewriter.getInstance().getSecuredUriBuilder(ub);
}
public static UriBuilder secure(UriInfo ui) {
return secure(ui.getAbsolutePathBuilder());
}
public static URI secure(URI uri) {
URI newURI = secure(fromUri(uri)).build();
log.debug("rewrote " + uri + " to " + newURI);
return newURI;
}
public static URI secure(URI base, String uri) {
URI newURI = secure(fromUri(base.resolve(uri))).build();
log.debug("rewrote " + uri + " to " + newURI);
return newURI;
}
/**
* A bean that allows configuration of how to rewrite generated URIs to be
* secure.
*
* @author Donal Fellows
*/
public static class Rewriter {
private static Rewriter instance;
private PortMapper portMapper;
private boolean suppress;
private String rewriteRE = "://[^/]+/[^/]+";
private String rewriteTarget;
static Rewriter getInstance() {
if (instance == null)
new Rewriter();
return instance;
}
@Autowired
public void setPortMapper(PortMapper portMapper) {
this.portMapper = portMapper;
}
/**
* Whether to suppress rewriting of URIs to be secure.
*
* @param suppressSecurity
* True if no rewriting should be done.
*/
public void setSuppressSecurity(boolean suppressSecurity) {
suppress = suppressSecurity;
}
public void setRewriteRegexp(String rewriteRegexp) {
this.rewriteRE = rewriteRegexp;
}
/**
* What to rewrite the host, port and web-app name to be.
*
* @param rewriteTarget
* What to rewrite to, or "<tt>NONE</tt>" for no rewrite.
*/
public void setRewriteTarget(String rewriteTarget) {
if (rewriteTarget.isEmpty())
this.rewriteTarget = null;
else if (rewriteTarget.equals("NONE"))
this.rewriteTarget = null;
else if (rewriteTarget.startsWith("${"))
this.rewriteTarget = null;
else
this.rewriteTarget = "://" + rewriteTarget;
}
private Integer lookupHttpsPort(URI uri) {
if (portMapper != null)
return portMapper.lookupHttpsPort(uri.getPort());
return null;
}
@SuppressWarnings
public Rewriter() {
instance = this;
}
@PreDestroy
@SuppressWarnings
public void done() {
instance = null;
Uri.log = null;
}
@NonNull
URI rewrite(@NonNull String url) {
if (rewriteTarget != null)
url = url.replaceFirst(rewriteRE, rewriteTarget);
return URI.create(url);
}
@NonNull
public UriBuilder getSecuredUriBuilder(@NonNull UriBuilder uribuilder) {
if (suppress)
return uribuilder.clone();
UriBuilder ub = new RewritingUriBuilder(uribuilder);
Integer secPort = null;
try {
secPort = lookupHttpsPort(ub.build());
} catch (Exception e) {
/*
* Do not log this; we know why it happens and don't actually
* care to do anything about it. All it does is fill up the log
* with pointless scariness!
*/
// log.debug("failed to extract current URI port", e);
}
if (secPort == null || secPort.intValue() == -1)
return ub.scheme(SECURE_SCHEME);
return ub.scheme(SECURE_SCHEME).port(secPort);
}
/**
* {@link UriBuilder} that applies a rewrite rule to the URIs produced
* by the wrapped builder.
*
* @author Donal Fellows
*/
class RewritingUriBuilder extends UriBuilder {
private UriBuilder wrapped;
RewritingUriBuilder(UriBuilder builder) {
wrapped = builder.clone();
}
private URI rewrite(URI uri) {
return Rewriter.this.rewrite(uri.toString());
}
@Override
public UriBuilder clone() {
return new RewritingUriBuilder(wrapped);
}
@Override
public URI buildFromMap(Map<String, ? extends Object> values)
throws IllegalArgumentException, UriBuilderException {
return rewrite(wrapped.buildFromMap(values));
}
@Override
public URI buildFromEncodedMap(Map<String, ? extends Object> values)
throws IllegalArgumentException, UriBuilderException {
return rewrite(wrapped.buildFromEncodedMap(values));
}
@Override
public URI build(Object... values) throws IllegalArgumentException,
UriBuilderException {
return rewrite(wrapped.build(values));
}
@Override
public URI buildFromEncoded(Object... values)
throws IllegalArgumentException, UriBuilderException {
return rewrite(wrapped.buildFromEncoded(values));
}
@Override
public UriBuilder uri(URI uri) throws IllegalArgumentException {
wrapped.uri(uri);
return this;
}
@Override
public UriBuilder scheme(String scheme)
throws IllegalArgumentException {
wrapped.scheme(scheme);
return this;
}
@Override
public UriBuilder schemeSpecificPart(String ssp)
throws IllegalArgumentException {
wrapped.schemeSpecificPart(ssp);
return this;
}
@Override
public UriBuilder userInfo(String ui) {
wrapped.userInfo(ui);
return this;
}
@Override
public UriBuilder host(String host) throws IllegalArgumentException {
wrapped.host(host);
return this;
}
@Override
public UriBuilder port(int port) throws IllegalArgumentException {
wrapped.port(port);
return this;
}
@Override
public UriBuilder replacePath(String path) {
wrapped.replacePath(path);
return this;
}
@Override
public UriBuilder path(String path) throws IllegalArgumentException {
wrapped.path(path);
return this;
}
@Override
public UriBuilder path(
@java.lang.SuppressWarnings("rawtypes") Class resource)
throws IllegalArgumentException {
wrapped.path(resource);
return this;
}
@Override
public UriBuilder path(
@java.lang.SuppressWarnings("rawtypes") Class resource,
String method) throws IllegalArgumentException {
wrapped.path(resource, method);
return this;
}
@Override
public UriBuilder path(Method method)
throws IllegalArgumentException {
wrapped.path(method);
return this;
}
@Override
public UriBuilder segment(String... segments)
throws IllegalArgumentException {
wrapped.segment(segments);
return this;
}
@Override
public UriBuilder replaceMatrix(String matrix)
throws IllegalArgumentException {
wrapped.replaceMatrix(matrix);
return this;
}
@Override
public UriBuilder matrixParam(String name, Object... values)
throws IllegalArgumentException {
wrapped.matrixParam(name, values);
return this;
}
@Override
public UriBuilder replaceMatrixParam(String name, Object... values)
throws IllegalArgumentException {
wrapped.replaceMatrixParam(name, values);
return this;
}
@Override
public UriBuilder replaceQuery(String query)
throws IllegalArgumentException {
wrapped.replaceQuery(query);
return this;
}
@Override
public UriBuilder queryParam(String name, Object... values)
throws IllegalArgumentException {
wrapped.queryParam(name, values);
return this;
}
@Override
public UriBuilder replaceQueryParam(String name, Object... values)
throws IllegalArgumentException {
wrapped.replaceQueryParam(name, values);
return this;
}
@Override
public UriBuilder fragment(String fragment) {
wrapped.fragment(fragment);
return this;
}
}
}
}