Author: chris.laprun(a)jboss.com
Date: 2009-01-31 16:20:08 -0500 (Sat, 31 Jan 2009)
New Revision: 12757
Modified:
modules/common/trunk/common/src/main/java/org/jboss/portal/common/util/ParameterValidation.java
modules/common/trunk/common/src/test/java/org/jboss/portal/test/common/util/ParameterValidationTestCase.java
Log:
- JBPORTAL-2291:
+ Added sanitize*WithHandler methods taking a ValidationErrorHandler as a parameter to
speficy the behavior to run in case of error.
+ Added LoggingValidationErrorHandler as a default ValidationErrorHandler.
+ Added related test cases.
Modified:
modules/common/trunk/common/src/main/java/org/jboss/portal/common/util/ParameterValidation.java
===================================================================
---
modules/common/trunk/common/src/main/java/org/jboss/portal/common/util/ParameterValidation.java 2009-01-31
19:50:20 UTC (rev 12756)
+++
modules/common/trunk/common/src/main/java/org/jboss/portal/common/util/ParameterValidation.java 2009-01-31
21:20:08 UTC (rev 12757)
@@ -22,6 +22,8 @@
******************************************************************************/
package org.jboss.portal.common.util;
+import org.apache.log4j.Logger;
+
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
@@ -34,7 +36,75 @@
public class ParameterValidation
{
public final static Pattern CSS_DISTANCE =
Pattern.compile("\\d+\\W*(em|ex|px|in|cm|mm|pt|pc|%)?");
+ public final static Pattern XSS_CHECK =
Pattern.compile("[^<>\\(\\)=]*");
+ /**
+ * Implements a behavior to be executed in case a value fails to be validated. Uses
the Chain of responsibility
+ * pattern so that several orthogonal behaviors can be chained if needed.
+ */
+ public abstract static class ValidationErrorHandler
+ {
+ private ValidationErrorHandler next;
+ private String defaultValue;
+ protected static final String CONTINUE = "__JBP__CONTINUE__";
+
+ public ValidationErrorHandler(String defaultValue)
+ {
+ this.defaultValue = defaultValue;
+ }
+
+ public ValidationErrorHandler setNext(ValidationErrorHandler next)
+ {
+ this.next = next;
+ return this;
+ }
+
+ public String handleValidationError(String failedValue)
+ {
+ String result = internalValidationErrorHandling(failedValue);
+ if (!CONTINUE.equals(result))
+ {
+ return result;
+ }
+ else if (next != null)
+ {
+ return next.handleValidationError(failedValue);
+ }
+ else
+ {
+ return defaultValue;
+ }
+
+ }
+
+ /**
+ * Decide what to do with the failed value.
+ *
+ * @param failedValue the value that failed passing validation
+ * @return a new value to be used by the client code or {@link #CONTINUE} if this
ValidationErrorHandler allows
+ * downstream handlers to further process the value.
+ */
+ protected abstract String internalValidationErrorHandling(String failedValue);
+ }
+
+ public static class LoggingValidationErrorHandler extends ValidationErrorHandler
+ {
+ private static final Logger log = Logger.getLogger(ParameterValidation.class);
+ private String contextMessage;
+
+ public LoggingValidationErrorHandler(String defaultValue, String contextMessage)
+ {
+ super(defaultValue);
+ this.contextMessage = contextMessage;
+ }
+
+ protected String internalValidationErrorHandling(String failedValue)
+ {
+ log.debug("'" + failedValue + "' failed validation."
+ (contextMessage != null ? "\n" + contextMessage : ""));
+ return CONTINUE;
+ }
+ }
+
public static void throwIllegalArgExceptionIfNullOrEmpty(String valueToCheck, String
valueName, String contextName)
{
if (isNullOrEmpty(valueToCheck))
@@ -99,11 +169,28 @@
*/
public static String sanitizeFromPattern(String value, Pattern regex, String
defaultValue)
{
+ return sanitizeFromPatternWithHandler(value, regex, new
LoggingValidationErrorHandler(defaultValue, null));
+ }
+
+ /**
+ * Checks if a given value matches the given format (as a regular expression) and
delegates what to do to the
+ * specified ValidationErrorHandler.
+ *
+ * @param value value to sanitize if needed, <code>null</code> is
considered as not matching
+ * @param regex format the value needs to conform to
+ * @param handler ValidationErrorHandler implementing the behavior to apply in case
the specified value failed to
+ * validate
+ * @return the specified value if it conforms to the expected format, whatever value
the specified
+ * ValidationErrorHandler thinks appropriate otherwise.
+ */
+ public static String sanitizeFromPatternWithHandler(String value, Pattern regex,
ValidationErrorHandler handler)
+ {
throwIllegalArgExceptionIfNull(regex, "expected value format");
+ throwIllegalArgExceptionIfNull(handler, "ValidationErrorHandler");
if (value == null || !regex.matcher(value).matches())
{
- return defaultValue;
+ return handler.handleValidationError(value);
}
else
{
@@ -122,12 +209,29 @@
*/
public static String sanitizeFromValues(String value, String[] possibleValues, String
defaultValue)
{
+ return sanitizeFromValuesWithHandler(value, possibleValues, new
LoggingValidationErrorHandler(defaultValue, null));
+ }
+
+ /**
+ * Checks if a given value matches one of the possible values and delegates what to do
to the specified
+ * ValidationErrorHandler.
+ *
+ * @param value value to sanitize if needed, <code>null</code> is
considered as not matching
+ * @param possibleValues possible values
+ * @param handler ValidationErrorHandler implementing the behavior to apply in
case the specified value failed
+ * to validate
+ * @return the specified value if it conforms to the expected format, whatever value
the specified
+ * ValidationErrorHandler thinks appropriate otherwise.
+ */
+ public static String sanitizeFromValuesWithHandler(String value, String[]
possibleValues, ValidationErrorHandler handler)
+ {
throwIllegalArgExceptionIfNullOrEmpty(possibleValues, "possible
values");
+ throwIllegalArgExceptionIfNull(handler, "ValidationErrorHandler");
List<String> values = Arrays.asList(possibleValues);
if (!values.contains(value))
{
- return defaultValue;
+ return handler.handleValidationError(value);
}
else
{
Modified:
modules/common/trunk/common/src/test/java/org/jboss/portal/test/common/util/ParameterValidationTestCase.java
===================================================================
---
modules/common/trunk/common/src/test/java/org/jboss/portal/test/common/util/ParameterValidationTestCase.java 2009-01-31
19:50:20 UTC (rev 12756)
+++
modules/common/trunk/common/src/test/java/org/jboss/portal/test/common/util/ParameterValidationTestCase.java 2009-01-31
21:20:08 UTC (rev 12757)
@@ -33,6 +33,14 @@
*/
public class ParameterValidationTestCase extends TestCase
{
+ private String appender;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ appender = "";
+ }
+
public void testSanitizeFromPatternNullPattern()
{
try
@@ -115,4 +123,86 @@
assertEquals("windowmove",
ParameterValidation.sanitizeFromValues("windowmove", possible, defaultValue));
assertEquals("windowremove",
ParameterValidation.sanitizeFromValues("windowremove", possible,
defaultValue));
}
+
+ public void testSanitizeNullHandler()
+ {
+ try
+ {
+ ParameterValidation.sanitizeFromPatternWithHandler("foo",
Pattern.compile(""), null);
+ fail("Should have thrown an IAE on null handler");
+ }
+ catch (Exception e)
+ {
+ // expected
+ }
+
+ try
+ {
+ ParameterValidation.sanitizeFromValuesWithHandler("foo", new
String[]{""}, null);
+ fail("Should have thrown an IAE on null handler");
+ }
+ catch (Exception e)
+ {
+ // expected
+ }
+ }
+
+ public void testSanitizeHandlerChain()
+ {
+ ParameterValidation.ValidationErrorHandler handler = new
TestValidationErrorHandler("default", "1");
+ handler.setNext(new TestValidationErrorHandler("default",
"2").setNext(new TestValidationErrorHandler("default",
"3")));
+
+ assertEquals("default",
ParameterValidation.sanitizeFromPatternWithHandler("foo",
Pattern.compile("bar"), handler));
+ assertEquals("1/2/3/", appender);
+
+ appender = "";
+ assertEquals("default",
ParameterValidation.sanitizeFromValuesWithHandler("foo", new
String[]{"bar"}, handler));
+ assertEquals("1/2/3/", appender);
+ }
+
+ public void testSanitizeInterruptedHandlerChain()
+ {
+ ParameterValidation.ValidationErrorHandler interruptor = new
InterruptingValidationErrorHandler("default");
+ ParameterValidation.ValidationErrorHandler handler = new
TestValidationErrorHandler("default", "1");
+ handler.setNext(interruptor.setNext(new
TestValidationErrorHandler("default", "3")));
+
+ assertNull(ParameterValidation.sanitizeFromPatternWithHandler("foo",
Pattern.compile("bar"), handler));
+ assertEquals("1/", appender);
+
+ appender = "";
+ assertNull(ParameterValidation.sanitizeFromValuesWithHandler("foo", new
String[]{"bar"}, handler));
+ assertEquals("1/", appender);
+ }
+
+ private class InterruptingValidationErrorHandler extends
ParameterValidation.ValidationErrorHandler
+ {
+ private InterruptingValidationErrorHandler(String defaultValue)
+ {
+ super(defaultValue);
+ }
+
+ @Override
+ protected String internalValidationErrorHandling(String failedValue)
+ {
+ return null;
+ }
+ }
+
+ private class TestValidationErrorHandler extends
ParameterValidation.ValidationErrorHandler
+ {
+ String order;
+
+ private TestValidationErrorHandler(String defaultValue, String order)
+ {
+ super(defaultValue);
+ this.order = order;
+ }
+
+ @Override
+ protected String internalValidationErrorHandling(String failedValue)
+ {
+ appender += order + "/";
+ return ParameterValidation.ValidationErrorHandler.CONTINUE;
+ }
+ }
}