
org.ocpsoft.rewrite.servlet.config.EncodeQuery Maven / Gradle / Ivy
/*
* Copyright 2011 Lincoln Baxter, III
*
* Licensed 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.ocpsoft.rewrite.servlet.config;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map.Entry;
import org.ocpsoft.common.util.Strings;
import org.ocpsoft.rewrite.config.Operation;
import org.ocpsoft.rewrite.context.EvaluationContext;
import org.ocpsoft.rewrite.event.Rewrite;
import org.ocpsoft.rewrite.servlet.RewriteWrappedRequest;
import org.ocpsoft.rewrite.servlet.config.encodequery.Base64EncodingStrategy;
import org.ocpsoft.rewrite.servlet.config.encodequery.ChecksumStrategy;
import org.ocpsoft.rewrite.servlet.config.encodequery.EncodingStrategy;
import org.ocpsoft.rewrite.servlet.config.encodequery.HashCodeChecksumStrategy;
import org.ocpsoft.rewrite.servlet.http.event.HttpInboundServletRewrite;
import org.ocpsoft.rewrite.servlet.http.event.HttpOutboundServletRewrite;
import org.ocpsoft.rewrite.servlet.util.QueryStringBuilder;
import org.ocpsoft.rewrite.servlet.util.URLBuilder;
import org.ocpsoft.rewrite.util.Maps;
import org.ocpsoft.urlbuilder.Address;
import org.ocpsoft.urlbuilder.AddressBuilder;
/**
* An {@link Operation} that encodes any or many {@link Address} query-parameters into a single parameter using the
* given {@link ChecksumStrategy} and {@link EncodingStrategy}. This can be used to effectively encrypt or obfuscate
* inbound and outbound query-parameters. Additionally, encoded parameters contain a checksum which can be used to
* reveal tampering, allowing for appropriate action to be taken on checksum verification failure.
*
*
* For example:
* ?c=LTg1NDM0OTA1OSM&lang=en_US
*
* The above query string contains multiple parameters. The value of parameter 'c' has been encoded using
* {@link EncodeQuery#to(String)} and has specified that lang
be excluded via {@link #excluding(String...)}.
*
* @author Lincoln Baxter, III
*
*/
@SuppressWarnings("deprecation")
public class EncodeQuery implements Operation
{
private String tokenName;
private ChecksumStrategy checksumStrategy = new HashCodeChecksumStrategy();
private EncodingStrategy encodingStrategy = new Base64EncodingStrategy();
private final List params = new ArrayList();
private final List excludedParams = new ArrayList();
private boolean inboundCorrection = true;
private Operation onfailure;
private EncodeQuery(final String[] params)
{
if ((params != null) && (params.length > 0))
this.params.addAll(Arrays.asList(params));
}
/**
* Create a new {@link EncodeQuery} operation for the given query-parameter names. Only encode those given
* parameters. If no parameter names are supplied, this will encode all query-parameters found in the {@link Address}
* .
*/
public static EncodeQuery params(final String... params)
{
return new EncodeQuery(params);
}
/**
* Exclude the given query-parameter names from encoding.
*/
public EncodeQuery excluding(final String... params)
{
if ((params != null) && (params.length > 0))
this.excludedParams.addAll(Arrays.asList(params));
return this;
}
/**
* Use the given {@link EncodingStrategy} when performing encoding.
*/
public EncodeQuery withEncodingStrategy(final EncodingStrategy strategy)
{
this.encodingStrategy = strategy;
return this;
}
/**
* Use the given {@link ChecksumStrategy} when verifying and embedding checksums.
*/
public EncodeQuery withChecksumStrategy(final ChecksumStrategy strategy)
{
this.checksumStrategy = strategy;
return this;
}
/**
* Redirect inbound requests to an {@link Address} containing matching query-parameters to the encoded
* {@link Address}.
*/
public EncodeQuery withInboundCorrection(final boolean enable)
{
inboundCorrection = enable;
return this;
}
/**
* {@link Operation} to be performed when the current {@link ChecksumStrategy} detects an inbound checksum failure.
*/
public EncodeQuery onChecksumFailure(final Operation operation)
{
this.onfailure = operation;
return this;
}
/**
* The name of the composite query-parameter to hold the encoded parameters.
*/
public EncodeQuery to(final String param)
{
this.tokenName = param;
return this;
}
@Override
public void perform(final Rewrite event, final EvaluationContext context)
{
if ((event instanceof HttpInboundServletRewrite) && DispatchType.isRequest().evaluate(event, context))
{
HttpInboundServletRewrite in = (HttpInboundServletRewrite) event;
QueryStringBuilder query = QueryStringBuilder.createNew();
query.addParameters(in.getInboundAddress().getQuery());
String token = query.decode().getParameter(tokenName);
if (token != null)
{
String decoded = encodingStrategy.decode(token);
if (checksumStrategy.checksumValid(decoded))
{
decoded = checksumStrategy.removeChecksum(decoded);
query.removeParameter(tokenName);
QueryStringBuilder queryParams = QueryStringBuilder.createFromEncoded(decoded);
RewriteWrappedRequest request = RewriteWrappedRequest.getCurrentInstance(in.getRequest());
for (Entry> param : queryParams.getParameterMap().entrySet()) {
for (String value : param.getValue()) {
Maps.addArrayValue(request.getModifiableParameters(), param.getKey(), value);
}
}
}
else if (onfailure != null)
{
onfailure.perform(event, context);
}
}
else if (!query.isEmpty() && inboundCorrection)
{
in.redirectTemporary(in.getAddress().getPathAndQuery());
}
}
else if (event instanceof HttpOutboundServletRewrite)
{
HttpOutboundServletRewrite out = (HttpOutboundServletRewrite) event;
String outboundURL = out.getOutboundAddress().toString();
URLBuilder url = URLBuilder.createFrom(outboundURL);
url.getQueryStringBuilder().removeParameter(tokenName);
QueryStringBuilder newQuery = QueryStringBuilder.createNew();
for (String param : excludedParams) {
newQuery.addParameter(param, url.getQueryStringBuilder().removeParameter(param).toArray(new String[] {}));
}
if (!params.isEmpty())
{
for (String param : url.getQueryStringBuilder().getParameterNames())
{
if (!params.contains(param))
{
newQuery.addParameter(param,
url.getQueryStringBuilder().removeParameter(param).toArray(new String[] {}));
}
}
}
if (outboundURL.contains("?") && (outboundURL.startsWith(out.getContextPath()) || outboundURL.startsWith("/")))
{
if (!url.getQueryStringBuilder().isEmpty())
{
String encoded = checksumStrategy.embedChecksum(url.getQueryStringBuilder().toQueryString());
encoded = encodingStrategy.encode(encoded);
newQuery.addParameter(tokenName, encoded);
out.setOutboundAddress(AddressBuilder.create(url.toPath() + newQuery.toQueryString()));
}
}
}
}
@Override
public String toString()
{
return "EncodeQuery.params(\"" + Strings.join(params, "\", \"") + "\")";
}
}