Преглед изворни кода

Finalizing the validation, entry point and channel processor concerning captchas. Replacing the Thread.sleep() in captchaChannelProcessorTest to avoid the build break issue.

Marc-Antoine Garrigue пре 20 година
родитељ
комит
60d3b6505b

+ 37 - 27
core/src/main/java/org/acegisecurity/captcha/CaptchaChannelProcessor.java

@@ -79,7 +79,7 @@ import org.springframework.util.Assert;
  * 
  * 
  * 
- * <li>{@link #getRequiresHumanUntilMaxRequestsKeyword()} <br>
+ * <li>{@link #getRequiresHumanAfterMaxRequestsKeyword()} <br>
  * default value = <code>REQUIRES_HUMAN_AFTER_MAX_MILLIS</code> <br>
  * if detected, checks if :
  * 
@@ -138,7 +138,7 @@ import org.springframework.util.Assert;
  * and the REQUIRES_HUMAN_AFTER_MAX_REQUESTS keywords <br>
  * with a maxRequestsBeforeReTest=20 <br>
  * and a maxMillisBeforeReTest=3600000 <br>
- * and amaxRequestsBeforeFirstTest=1000</li>
+ * and a maxRequestsBeforeFirstTest=1000</li>
  * 
  * </ul>
  * 
@@ -158,7 +158,10 @@ public class CaptchaChannelProcessor implements ChannelProcessor,
 
 	private String requiresHumanAfterMaxMillisKeyword = "REQUIRES_HUMAN_AFTER_MAX_MILLIS";
 
-	private ChannelEntryPoint entryPoint;
+    private String keywordPrefix = "";
+
+
+    private ChannelEntryPoint entryPoint;
 
 	private int maxRequestsBeforeReTest = -1;
 
@@ -166,7 +169,15 @@ public class CaptchaChannelProcessor implements ChannelProcessor,
 
 	private long maxMillisBeforeReTest = -1;
 
-	public String getRequiresHumanAfterMaxMillisKeyword() {
+    public String getKeywordPrefix() {
+        return keywordPrefix;
+    }
+
+    public void setKeywordPrefix(String keywordPrefix) {
+        this.keywordPrefix = keywordPrefix;
+    }
+
+    public String getRequiresHumanAfterMaxMillisKeyword() {
 		return requiresHumanAfterMaxMillisKeyword;
 	}
 
@@ -227,28 +238,21 @@ public class CaptchaChannelProcessor implements ChannelProcessor,
 		if ((invocation == null) || (config == null)) {
 			throw new IllegalArgumentException("Nulls cannot be provided");
 		}
-		CaptchaSecurityContext context = (CaptchaSecurityContext) SecurityContextHolder
-				.getContext();
+        CaptchaSecurityContext context = null;
+        context = (CaptchaSecurityContext) SecurityContextHolder
+                    .getContext();
 
-		Iterator iter = config.getConfigAttributes();
-		boolean shouldRedirect = true;
+        Iterator iter = config.getConfigAttributes();
+		boolean shouldRedirect = false;
 
 		while (iter.hasNext()) {
 			ConfigAttribute attribute = (ConfigAttribute) iter.next();
-
 			if (supports(attribute)) {
 				logger.debug("supports this attribute : " + attribute);
-				if (isContextValidForAttribute(context, attribute)) {
-					shouldRedirect = false;
-				} else {
-					// reset if already passed a constraint
-
+				if (!isContextValidForAttribute(context, attribute)) {
 					shouldRedirect = true;
-					// break at first unsatisfy contraint
-					break;
-				}
-
-			}
+                }
+            }
 		}
 		if (shouldRedirect) {
 			logger
@@ -270,8 +274,7 @@ public class CaptchaChannelProcessor implements ChannelProcessor,
 		if ((attribute != null) || (attribute.getAttribute() != null)) {
 
 			// test the REQUIRES_HUMAN_AFTER_MAX_REQUESTS keyword
-			if (attribute.getAttribute().equals(
-					getRequiresHumanAfterMaxRequestsKeyword())) {
+			if (isKeywordMaxRequest(attribute)) {
 				if (isContextValidConcerningHumanOrFirstTest(context)
 						&& isContextValidConcerningReTest(context)) {
 					valid = true;
@@ -279,8 +282,7 @@ public class CaptchaChannelProcessor implements ChannelProcessor,
 			}
 
 			// test the REQUIRES_HUMAN_AFTER_MAX_MILLIS keyword
-			if (attribute.getAttribute().equals(
-					getRequiresHumanAfterMaxMillisKeyword())) {
+			if (isKeywordMillis(attribute)) {
 				if (isContextValidConcerningHumanOrFirstTest(context)
 						&& isContextValidConcerningMaxMillis(context)) {
 					valid = true;
@@ -346,10 +348,7 @@ public class CaptchaChannelProcessor implements ChannelProcessor,
 	public boolean supports(ConfigAttribute attribute) {
 		if ((attribute != null)
 				&& (attribute.getAttribute() != null)
-				&& (attribute.getAttribute().equals(
-						getRequiresHumanAfterMaxRequestsKeyword()) || attribute
-						.getAttribute().equals(
-								getRequiresHumanAfterMaxMillisKeyword())
+				&& (isKeywordMaxRequest(attribute) || isKeywordMillis(attribute)
 
 				)) {
 			return true;
@@ -358,4 +357,15 @@ public class CaptchaChannelProcessor implements ChannelProcessor,
 		}
 	}
 
+    private boolean isKeywordMillis(ConfigAttribute attribute) {
+        return attribute
+                .getAttribute().equals(
+                        getKeywordPrefix()+getRequiresHumanAfterMaxMillisKeyword());
+    }
+
+    private boolean isKeywordMaxRequest(ConfigAttribute attribute) {
+        return attribute.getAttribute().equals(
+                getKeywordPrefix()+getRequiresHumanAfterMaxRequestsKeyword());
+    }
+
 }

+ 343 - 252
core/src/main/java/org/acegisecurity/captcha/CaptchaEntryPoint.java

@@ -14,274 +14,365 @@
  */
 package net.sf.acegisecurity.captcha;
 
-import java.io.IOException;
-import java.util.Enumeration;
-
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
 import net.sf.acegisecurity.securechannel.ChannelEntryPoint;
 import net.sf.acegisecurity.util.PortMapper;
 import net.sf.acegisecurity.util.PortMapperImpl;
 import net.sf.acegisecurity.util.PortResolver;
 import net.sf.acegisecurity.util.PortResolverImpl;
-
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.util.Assert;
 
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Enumeration;
+
 /**
  * The captcha entry point : redirect to the captcha test page. <br/>
- * 
+ * <p/>
  * This entry point can force the use of SSL : see {@link #getForceHttps()}<br/>
- * 
- * This entry point allows internal OR external redirect : see
- * {@link #setOutsideWebApp(boolean)}<br/>/ Original request can be added to
- * the redirect path using a special parameter : see
- * {@link #getOriginalRequestParameterName()} and
- * {@link #setIncludeOriginalRequest()} <br/> <br/> Default values :<br/>
- * forceHttps = false<br/> includesOriginalRequest = false<br/>
- * originalRequestParameterName= "originalRequest"<br/> isOutsideWebApp=false<br/>
- * 
+ * <p/>
+ * This entry point allows internal OR external redirect : see {@link #setOutsideWebApp(boolean)}<br/>/ Original request
+ * can be added to the redirect path using a custom translation : see {@link #setIncludeOriginalRequest(boolean)} <br/>
+ * Original request is translated using URLEncoding and the following translation mapping in the redirect url : <ul>
+ * <li>original url => {@link #getOriginalRequestUrlParameterName()}</li> <li> If {@link
+ * #isIncludeOriginalParameters()}</li> <li>original method => {@link #getOriginalRequestMethodParameterName()} </li>
+ * <li>original parameters => {@link #getOriginalRequestParametersParameterName()} </li> <li>The orinial parameters
+ * string is contructed using :</li> <ul> <li>a parameter separator {@link #getOriginalRequestParametersSeparator()}
+ * </li> <li>a parameter name value pair separator for each parameter {@link #getOriginalRequestParametersNameValueSeparator()}
+ * </li> </ul> </ul>
+ * <p/>
+ * <p/>
+ * <p/>
+ * <br/> Default values :<br/> forceHttps = false<br/> includesOriginalRequest = true<br/> includesOriginalParameters =
+ * false<br/> isOutsideWebApp=false<br/> originalRequestUrlParameterName  ="original_requestUrl" <br/>
+ * originalRequestParametersParameterName = "original_request_parameters";<br/>
+ * <p/>
+ * originalRequestParametersNameValueSeparator = "@@";        <br/>
+ * <p/>
+ * originalRequestParametersSeparator = ";;";         <br/>
+ * <p/>
+ * originalRequestMethodParameterName = "original_request_method";  <br/>
+ * <p/>
+ * urlEncodingCharset = "UTF-8";             <br/>
+ *
  * @author marc antoine Garrigue
  * @version $Id$
  */
 public class CaptchaEntryPoint implements ChannelEntryPoint, InitializingBean {
-	// ~ Static fields/initializers
-	// =============================================
-
-	private static final Log logger = LogFactory
-			.getLog(CaptchaEntryPoint.class);
-
-	// ~ Instance fields
-	// ========================================================
-
-	private PortMapper portMapper = new PortMapperImpl();
-
-	private PortResolver portResolver = new PortResolverImpl();
-
-	private String captchaFormUrl;
-
-	private boolean forceHttps = false;
-
-	private String originalRequestParameterName = "originalRequest";
-
-	private boolean isOutsideWebApp = false;
-
-	private boolean includeOriginalRequest = false;
-
-	// ~ Methods
-	// ================================================================
-
-	/**
-	 * Set to true to force captcha form access to be via https. If this value
-	 * is ture (the default is false), and the incoming request for the
-	 * protected resource which triggered the interceptor was not already
-	 * <code>https</code>, then
-	 * 
-	 * @param forceHttps
-	 */
-	public void setForceHttps(boolean forceHttps) {
-		this.forceHttps = forceHttps;
-	}
-
-	public boolean getForceHttps() {
-		return forceHttps;
-	}
-
-	/**
-	 * The URL where the <code>CaptchaProcessingFilter</code> login page can
-	 * be found. Should be relative to the web-app context path, and include a
-	 * leading <code>/</code>
-	 * 
-	 * @param captchaFormUrl
-	 */
-	public void setCaptchaFormUrl(String loginFormUrl) {
-		this.captchaFormUrl = loginFormUrl;
-	}
-
-	/**
-	 * @return the captcha test page to redirect to.
-	 */
-	public String getCaptchaFormUrl() {
-		return captchaFormUrl;
-	}
-
-	public void setPortMapper(PortMapper portMapper) {
-		this.portMapper = portMapper;
-	}
-
-	public PortMapper getPortMapper() {
-		return portMapper;
-	}
-
-	public void setPortResolver(PortResolver portResolver) {
-		this.portResolver = portResolver;
-	}
-
-	public PortResolver getPortResolver() {
-		return portResolver;
-	}
-
-	public boolean isOutsideWebApp() {
-		return isOutsideWebApp;
-	}
-
-	/**
-	 * if set to true, the {@link #commence(ServletRequest, ServletResponse)}
-	 * method uses the {@link #getCaptchaFormUrl()} as a complete URL, else it
-	 * as a 'inside WebApp' path.
-	 * 
-	 * @param isOutsideWebApp
-	 */
-	public void setOutsideWebApp(boolean isOutsideWebApp) {
-		this.isOutsideWebApp = isOutsideWebApp;
-	}
-
-	public String getOriginalRequestParameterName() {
-		return originalRequestParameterName;
-	}
-
-	/**
-	 * sets the parameter under which the original request url will be appended
-	 * to the redirect url (only if {@link #isIncludeOriginalRequest()}==true).
-	 * 
-	 * @param originalRequestParameterName
-	 */
-	public void setOriginalRequestParameterName(
-			String originalRequestParameterName) {
-		this.originalRequestParameterName = originalRequestParameterName;
-	}
-
-	public boolean isIncludeOriginalRequest() {
-		return includeOriginalRequest;
-	}
-
-	/**
-	 * If set to true, the original request url will be appended to the redirect
-	 * url using the {@link #getOriginalRequestParameterName()}.
-	 * 
-	 * @param includeOriginalRequest
-	 */
-	public void setIncludeOriginalRequest(boolean includeOriginalRequest) {
-		this.includeOriginalRequest = includeOriginalRequest;
-	}
-
-	public void afterPropertiesSet() throws Exception {
-		Assert.hasLength(captchaFormUrl, "captchaFormUrl must be specified");
-		Assert.notNull(portMapper, "portMapper must be specified");
-		Assert.notNull(portResolver, "portResolver must be specified");
-	}
-
-	public void commence(ServletRequest request, ServletResponse response)
-			throws IOException, ServletException {
-		StringBuffer redirectUrl = new StringBuffer();
-		HttpServletRequest req = (HttpServletRequest) request;
-
-		if (isOutsideWebApp) {
-			redirectUrl = redirectUrl.append(captchaFormUrl);
-		} else {
-			buildInternalRedirect(redirectUrl, req);
-		}
-
-		if (includeOriginalRequest) {
-			includeOriginalRequest(redirectUrl, req);
-		}
-		// add post parameter? TODO?
-		if (logger.isDebugEnabled()) {
-			logger.debug("Redirecting to: " + redirectUrl);
-		}
-
-		((HttpServletResponse) response)
-				.sendRedirect(((HttpServletResponse) response)
-						.encodeRedirectURL(redirectUrl.toString()));
-	}
-
-	private void includeOriginalRequest(StringBuffer redirectUrl,
-			HttpServletRequest req) {
-		// add original request to the url
-		if (redirectUrl.indexOf("?") >= 0) {
-			redirectUrl.append("&");
-		} else {
-			redirectUrl.append("?");
-		}
-		redirectUrl.append(originalRequestParameterName);
-		redirectUrl.append("=");
-		redirectUrl.append(req.getRequestURL().toString());
-		// append query params
-		Enumeration parameters = req.getParameterNames();
-		if (parameters != null && parameters.hasMoreElements()) {
-			redirectUrl.append("?");
-			while (parameters.hasMoreElements()) {
-				String name = parameters.nextElement().toString();
-				String value = req.getParameter(name);
-				redirectUrl.append(name);
-				redirectUrl.append("=");
-				redirectUrl.append(value);
-				if (parameters.hasMoreElements()) {
-					redirectUrl.append("&");
-				}
-			}
-		}
-
-	}
-
-	private void buildInternalRedirect(StringBuffer redirectUrl,
-			HttpServletRequest req) {
-		// construct it
-		StringBuffer simpleRedirect = new StringBuffer();
-
-		String scheme = req.getScheme();
-		String serverName = req.getServerName();
-		int serverPort = portResolver.getServerPort(req);
-		String contextPath = req.getContextPath();
-		boolean includePort = true;
-		if ("http".equals(scheme.toLowerCase()) && (serverPort == 80)) {
-			includePort = false;
-		}
-		if ("https".equals(scheme.toLowerCase()) && (serverPort == 443)) {
-			includePort = false;
-		}
-
-		simpleRedirect.append(scheme);
-		simpleRedirect.append("://");
-		simpleRedirect.append(serverName);
-		if (includePort) {
-			simpleRedirect.append(":");
-			simpleRedirect.append(serverPort);
-		}
-		simpleRedirect.append(contextPath);
-		simpleRedirect.append(captchaFormUrl);
-
-		if (forceHttps && req.getScheme().equals("http")) {
-			Integer httpPort = new Integer(portResolver.getServerPort(req));
-			Integer httpsPort = (Integer) portMapper.lookupHttpsPort(httpPort);
-
-			if (httpsPort != null) {
-				if (httpsPort.intValue() == 443) {
-					includePort = false;
-				} else {
-					includePort = true;
-				}
-
-				redirectUrl.append("https://");
-				redirectUrl.append(serverName);
-				if (includePort) {
-					redirectUrl.append(":");
-					redirectUrl.append(httpsPort);
-				}
-				redirectUrl.append(contextPath);
-				redirectUrl.append(captchaFormUrl);
-			} else {
-				redirectUrl.append(simpleRedirect);
-			}
-		} else {
-			redirectUrl.append(simpleRedirect);
-		}
-	}
+    // ~ Static fields/initializers
+    // =============================================
+
+    private static final Log logger = LogFactory
+            .getLog(CaptchaEntryPoint.class);
+
+    // ~ Instance fields
+    // ========================================================
+
+    private PortMapper portMapper = new PortMapperImpl();
+
+    private PortResolver portResolver = new PortResolverImpl();
+
+    private String captchaFormUrl;
+
+    private boolean forceHttps = false;
+
+    private String originalRequestUrlParameterName = "original_requestUrl";
+
+    private String originalRequestParametersParameterName = "original_request_parameters";
+
+    private String originalRequestParametersNameValueSeparator = "@@";
+
+    private String originalRequestParametersSeparator = ";;";
+
+    private String originalRequestMethodParameterName = "original_request_method";
+
+    private String urlEncodingCharset = "UTF-8";
+
+    private boolean isOutsideWebApp = false;
+
+    private boolean includeOriginalRequest = true;
+
+    private boolean includeOriginalParameters = false;
+
+    // ~ Methods
+    // ================================================================
+
+    /**
+     * Set to true to force captcha form access to be via https. If this value is ture (the default is false), and the
+     * incoming request for the protected resource which triggered the interceptor was not already <code>https</code>,
+     * then
+     */
+    public void setForceHttps(boolean forceHttps) {
+        this.forceHttps = forceHttps;
+    }
+
+    public boolean getForceHttps() {
+        return forceHttps;
+    }
+
+    /**
+     * The URL where the <code>CaptchaProcessingFilter</code> login page can be found. Should be relative to the web-app
+     * context path, and include a leading <code>/</code>
+     */
+    public void setCaptchaFormUrl(String captchaFormUrl) {
+        this.captchaFormUrl = captchaFormUrl;
+    }
+
+    /**
+     * @return the captcha test page to redirect to.
+     */
+    public String getCaptchaFormUrl() {
+        return captchaFormUrl;
+    }
+
+    public void setPortMapper(PortMapper portMapper) {
+        this.portMapper = portMapper;
+    }
+
+    public PortMapper getPortMapper() {
+        return portMapper;
+    }
+
+    public void setPortResolver(PortResolver portResolver) {
+        this.portResolver = portResolver;
+    }
+
+    public PortResolver getPortResolver() {
+        return portResolver;
+    }
+
+
+    public boolean isOutsideWebApp() {
+        return isOutsideWebApp;
+    }
+
+
+    public String getOriginalRequestUrlParameterName() {
+        return originalRequestUrlParameterName;
+    }
+
+    public void setOriginalRequestUrlParameterName(String originalRequestUrlParameterName) {
+        this.originalRequestUrlParameterName = originalRequestUrlParameterName;
+    }
+
+    public String getOriginalRequestParametersParameterName() {
+        return originalRequestParametersParameterName;
+    }
+
+    public void setOriginalRequestParametersParameterName(String originalRequestParametersParameterName) {
+        this.originalRequestParametersParameterName = originalRequestParametersParameterName;
+    }
+
+    public String getOriginalRequestParametersNameValueSeparator() {
+        return originalRequestParametersNameValueSeparator;
+    }
+
+    public void setOriginalRequestParametersNameValueSeparator(String originalRequestParametersNameValueSeparator) {
+        this.originalRequestParametersNameValueSeparator = originalRequestParametersNameValueSeparator;
+    }
+
+    public String getOriginalRequestParametersSeparator() {
+        return originalRequestParametersSeparator;
+    }
+
+    public void setOriginalRequestParametersSeparator(String originalRequestParametersSeparator) {
+        this.originalRequestParametersSeparator = originalRequestParametersSeparator;
+    }
+
+    public String getOriginalRequestMethodParameterName() {
+        return originalRequestMethodParameterName;
+    }
+
+    public void setOriginalRequestMethodParameterName(String originalRequestMethodParameterName) {
+        this.originalRequestMethodParameterName = originalRequestMethodParameterName;
+    }
+
+    public String getUrlEncodingCharset() {
+        return urlEncodingCharset;
+    }
+
+    public void setUrlEncodingCharset(String urlEncodingCharset) {
+        this.urlEncodingCharset = urlEncodingCharset;
+    }
+
+    /**
+     * if set to true, the {@link #commence(ServletRequest, ServletResponse)} method uses the {@link
+     * #getCaptchaFormUrl()} as a complete URL, else it as a 'inside WebApp' path.
+     */
+    public void setOutsideWebApp(boolean isOutsideWebApp) {
+        this.isOutsideWebApp = isOutsideWebApp;
+    }
+
+
+    public boolean isIncludeOriginalRequest() {
+        return includeOriginalRequest;
+    }
+
+    /**
+     * If set to true, the original request url will be appended to the redirect url using the {@link
+     * #getOriginalRequestParameterName()}.
+     */
+    public void setIncludeOriginalRequest(boolean includeOriginalRequest) {
+        this.includeOriginalRequest = includeOriginalRequest;
+    }
+
+    public boolean isIncludeOriginalParameters() {
+        return includeOriginalParameters;
+    }
+
+    public void setIncludeOriginalParameters(boolean includeOriginalParameters) {
+        this.includeOriginalParameters = includeOriginalParameters;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        Assert.hasLength(captchaFormUrl, "captchaFormUrl must be specified");
+        Assert.hasLength(originalRequestMethodParameterName, "originalRequestMethodParameterName must be specified");
+        Assert.hasLength(originalRequestParametersNameValueSeparator, "originalRequestParametersNameValueSeparator must be specified");
+        Assert.hasLength(originalRequestParametersParameterName, "originalRequestParametersParameterName must be specified");
+        Assert.hasLength(originalRequestParametersSeparator, "originalRequestParametersSeparator must be specified");
+        Assert.hasLength(originalRequestUrlParameterName, "originalRequestUrlParameterName must be specified");
+        Assert.hasLength(urlEncodingCharset, "urlEncodingCharset must be specified");
+        Assert.notNull(portMapper, "portMapper must be specified");
+        Assert.notNull(portResolver, "portResolver must be specified");
+        URLEncoder.encode("   fzaef é& à ", urlEncodingCharset);
+    }
+
+    public void commence(ServletRequest request, ServletResponse response)
+            throws IOException, ServletException {
+        StringBuffer redirectUrl = new StringBuffer();
+        HttpServletRequest req = (HttpServletRequest) request;
+
+        if (isOutsideWebApp) {
+            redirectUrl = redirectUrl.append(captchaFormUrl);
+        } else {
+            buildInternalRedirect(redirectUrl, req);
+        }
+
+        if (includeOriginalRequest) {
+            includeOriginalRequest(redirectUrl, req);
+        }
+        // add post parameter? DONE!
+        if (logger.isDebugEnabled()) {
+            logger.debug("Redirecting to: " + redirectUrl);
+        }
+
+        ((HttpServletResponse) response)
+                .sendRedirect(redirectUrl.toString());
+    }
+
+    private void includeOriginalRequest(StringBuffer redirectUrl,
+                                        HttpServletRequest req) {
+        // add original request to the url
+        if (redirectUrl.indexOf("?") >= 0) {
+            redirectUrl.append("&");
+        } else {
+            redirectUrl.append("?");
+        }
+
+        redirectUrl.append(originalRequestUrlParameterName);
+        redirectUrl.append("=");
+        try {
+            redirectUrl.append(URLEncoder.encode(req.getRequestURL().toString(), urlEncodingCharset));
+        } catch (UnsupportedEncodingException e) {
+            logger.warn(e);
+        }
+
+        //append method
+        redirectUrl.append("&");
+        redirectUrl.append(originalRequestMethodParameterName);
+        redirectUrl.append("=");
+        redirectUrl.append(req.getMethod());
+        if (includeOriginalParameters) {
+
+            // append query params
+
+            redirectUrl.append("&");
+            redirectUrl.append(originalRequestParametersParameterName);
+            redirectUrl.append("=");
+            StringBuffer qp = new StringBuffer();
+            Enumeration parameters = req.getParameterNames();
+            if (parameters != null && parameters.hasMoreElements()) {
+                //qp.append("?");
+                while (parameters.hasMoreElements()) {
+                    String name = parameters.nextElement().toString();
+                    String value = req.getParameter(name);
+                    qp.append(name);
+                    qp.append(originalRequestParametersNameValueSeparator);
+                    qp.append(value);
+                    if (parameters.hasMoreElements()) {
+                        qp.append(originalRequestParametersSeparator);
+                    }
+                }
+            }
+            try {
+                redirectUrl.append(URLEncoder.encode(qp.toString(), urlEncodingCharset));
+            } catch (Exception e) {
+                logger.warn(e);
+            }
+        }
+
+    }
+
+    private void buildInternalRedirect(StringBuffer redirectUrl,
+                                       HttpServletRequest req) {
+        // construct it
+        StringBuffer simpleRedirect = new StringBuffer();
+
+        String scheme = req.getScheme();
+        String serverName = req.getServerName();
+        int serverPort = portResolver.getServerPort(req);
+        String contextPath = req.getContextPath();
+        boolean includePort = true;
+        if ("http".equals(scheme.toLowerCase()) && (serverPort == 80)) {
+            includePort = false;
+        }
+        if ("https".equals(scheme.toLowerCase()) && (serverPort == 443)) {
+            includePort = false;
+        }
+
+        simpleRedirect.append(scheme);
+        simpleRedirect.append("://");
+        simpleRedirect.append(serverName);
+        if (includePort) {
+            simpleRedirect.append(":");
+            simpleRedirect.append(serverPort);
+        }
+        simpleRedirect.append(contextPath);
+        simpleRedirect.append(captchaFormUrl);
+
+        if (forceHttps && req.getScheme().equals("http")) {
+            Integer httpPort = new Integer(portResolver.getServerPort(req));
+            Integer httpsPort = (Integer) portMapper.lookupHttpsPort(httpPort);
+
+            if (httpsPort != null) {
+                if (httpsPort.intValue() == 443) {
+                    includePort = false;
+                } else {
+                    includePort = true;
+                }
+
+                redirectUrl.append("https://");
+                redirectUrl.append(serverName);
+                if (includePort) {
+                    redirectUrl.append(":");
+                    redirectUrl.append(httpsPort);
+                }
+                redirectUrl.append(contextPath);
+                redirectUrl.append(captchaFormUrl);
+            } else {
+                redirectUrl.append(simpleRedirect);
+            }
+        } else {
+            redirectUrl.append(simpleRedirect);
+        }
+    }
 
 }

+ 1 - 3
core/src/main/java/org/acegisecurity/captcha/CaptchaSecurityContext.java

@@ -30,13 +30,11 @@ public interface CaptchaSecurityContext extends SecurityContext {
 
 	/**
 	 * set human attribute, should called after captcha validation.
-	 * 
-	 * @param human
 	 */
 	void setHuman();
 
 	/**
-	 * 
+	 *
 	 * @return number of human restricted resources requests since the last
 	 *         passed captcha.
 	 */

+ 19 - 2
core/src/main/java/org/acegisecurity/captcha/CaptchaServiceProxy.java

@@ -1,3 +1,18 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * 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 net.sf.acegisecurity.captcha;
 
 import javax.servlet.ServletRequest;
@@ -11,7 +26,9 @@ import javax.servlet.ServletRequest;
 public interface CaptchaServiceProxy {
 
 	/**
-	 * @return true if the request is validated by the back end captcha service.
+     * @param id the id token
+     * @param captchaResponse the user response
+	 * @return true if the response is validated by the back end captcha service.
 	 */
-	boolean validateRequest(ServletRequest request);
+	boolean validateReponseForId(String id , Object captchaResponse);
 }

+ 113 - 97
core/src/main/java/org/acegisecurity/captcha/CaptchaValidationProcessingFilter.java

@@ -15,112 +15,128 @@
 
 package net.sf.acegisecurity.captcha;
 
-import java.io.IOException;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-
 import net.sf.acegisecurity.context.HttpSessionContextIntegrationFilter;
 import net.sf.acegisecurity.context.SecurityContextHolder;
-
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.springframework.beans.factory.InitializingBean;
 
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+
 /**
- * Filter for web integration of the {@link CaptchaServiceProxy}. <br/> It
- * basically intercept calls containing the specific validation parameter, use
- * the {@link CaptchaServiceProxy} to validate the request, and update the
- * {@link CaptchaSecurityContext} if the request passed the validation. <br/>
- * <br/> This Filter should be placed after the ContextIntegration filter and
- * before the {@link CaptchaChannelProcessor} filter in the filter stack in
- * order to update the {@link CaptchaSecurityContext} before the humanity
- * verification routine occurs. <br/> <br/> This filter should only be used in
- * conjunction with the {@link CaptchaSecurityContext} <br/> <br/>
- * 
- * 
+ * Filter for web integration of the {@link CaptchaServiceProxy}. <br/> It basically intercept calls containing the
+ * specific validation parameter, use the {@link CaptchaServiceProxy} to validate the request, and update the {@link
+ * CaptchaSecurityContext} if the request passed the validation. <br/> <br/> This Filter should be placed after the
+ * ContextIntegration filter and before the {@link CaptchaChannelProcessor} filter in the filter stack in order to
+ * update the {@link CaptchaSecurityContext} before the humanity verification routine occurs. <br/> <br/> This filter
+ * should only be used in conjunction with the {@link CaptchaSecurityContext} <br/> <br/>
+ *
  * @author marc antoine Garrigue
  * @version $Id$
  */
 public class CaptchaValidationProcessingFilter implements InitializingBean,
-		Filter {
-	// ~ Static fields/initializers
-	// =============================================
-	public static String CAPTCHA_VALIDATION_SECURITY_PARAMETER_KEY = "_captcha_parameter";
-
-	protected static final Log logger = LogFactory
-			.getLog(HttpSessionContextIntegrationFilter.class);
-
-	// ~ Instance fields
-	// ========================================================
-
-	private CaptchaServiceProxy captchaService;
-
-	// ~ Methods
-	// ================================================================
-
-	public CaptchaServiceProxy getCaptchaService() {
-		return captchaService;
-	}
-
-	public void setCaptchaService(CaptchaServiceProxy captchaService) {
-		this.captchaService = captchaService;
-	}
-
-	public void afterPropertiesSet() throws Exception {
-		if (this.captchaService == null) {
-			throw new IllegalArgumentException(
-					"CaptchaServiceProxy must be defined ");
-		}
-	}
-
-	/**
-	 * Does nothing. We use IoC container lifecycle services instead.
-	 */
-	public void destroy() {
-	}
-
-	public void doFilter(ServletRequest request, ServletResponse response,
-			FilterChain chain) throws IOException, ServletException {
-
-		if ((request != null)
-				&& (request
-						.getParameter(CAPTCHA_VALIDATION_SECURITY_PARAMETER_KEY) != null)) {
-			logger.debug("captcha validation parameter not found, do nothing");
-			// validate the request against CaptchaServiceProxy
-			boolean valid = false;
-
-			logger.debug("try to validate");
-			valid = this.captchaService.validateRequest(request);
-			logger.debug("captchaServiceProxy says : request is valid ="
-					+ valid);
-			if (valid) {
-				logger.debug("update the context");
-				((CaptchaSecurityContext) SecurityContextHolder.getContext())
-						.setHuman();
-
-			}
-
-		} else {
-			logger.debug("captcha validation parameter not found, do nothing");
-		}
-		logger.debug("chain...");
-		chain.doFilter(request, response);
-	}
-
-	/**
-	 * Does nothing. We use IoC container lifecycle services instead.
-	 * 
-	 * @param filterConfig
-	 *            ignored
-	 * 
-	 * @throws ServletException
-	 *             ignored
-	 */
-	public void init(FilterConfig filterConfig) throws ServletException {
-	}
+        Filter {
+    // ~ Static fields/initializers
+    // =============================================
+    protected static final Log logger = LogFactory
+            .getLog(CaptchaValidationProcessingFilter.class);
+
+    // ~ Instance fields
+    // ========================================================
+
+    private CaptchaServiceProxy captchaService;
+    private String captchaValidationParameter = "_captcha_parameter";
+
+
+    // ~ Methods
+    // ================================================================
+
+    public CaptchaServiceProxy getCaptchaService() {
+        return captchaService;
+    }
+
+    public void setCaptchaService(CaptchaServiceProxy captchaService) {
+        this.captchaService = captchaService;
+    }
+
+
+    public String getCaptchaValidationParameter() {
+        return captchaValidationParameter;
+    }
+
+    public void setCaptchaValidationParameter(String captchaValidationParameter) {
+        this.captchaValidationParameter = captchaValidationParameter;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        if (this.captchaService == null) {
+            throw new IllegalArgumentException(
+                    "CaptchaServiceProxy must be defined ");
+        }
+        if (this.captchaValidationParameter == null||"".equals(captchaValidationParameter)) {
+            throw new IllegalArgumentException(
+                    "captchaValidationParameter must not be empty or null");
+        }
+    }
+
+    /**
+     * Does nothing. We use IoC container lifecycle services instead.
+     */
+    public void destroy() {
+    }
+
+    public void doFilter(ServletRequest request, ServletResponse response,
+                         FilterChain chain) throws IOException, ServletException {
+        String captcha_reponse = request
+                .getParameter(captchaValidationParameter);
+        if ((request != null) && request instanceof HttpServletRequest
+                && ( captcha_reponse!= null)) {
+
+            logger.debug("captcha validation parameter found");
+            // validate the request against CaptchaServiceProxy
+            boolean valid = false;
+
+            logger.debug("try to validate");
+
+            //get session
+            HttpSession session = ((HttpServletRequest) request).getSession();
+            if (session != null) {
+
+                String id = session.getId();
+                valid = this.captchaService.validateReponseForId(id,
+                        captcha_reponse);
+                logger.debug("captchaServiceProxy says : request is valid = "
+                        + valid);
+                if (valid) {
+                    logger.debug("update the context");
+                    ((CaptchaSecurityContext) SecurityContextHolder.getContext())
+                            .setHuman();
+                    //logger.debug("retrieve original request from ")
+
+                }else{
+                  logger.debug("captcha test failed");
+                }
+
+            }else{
+                logger.debug("no session found, user don't even ask a captcha challenge");
+            }
+        } else {
+           logger.debug("captcha validation parameter not found, do nothing");
+        }
+        logger.debug("chain ...");
+        chain.doFilter(request, response);
+    }
+
+    /**
+     * Does nothing. We use IoC container lifecycle services instead.
+     *
+     * @param filterConfig ignored
+     *
+     * @throws ServletException ignored
+     */
+    public void init(FilterConfig filterConfig) throws ServletException {
+    }
 }

+ 1 - 1
core/src/main/java/org/acegisecurity/ui/session/HttpSessionEventPublisher.java

@@ -119,7 +119,7 @@ public class HttpSessionEventPublisher implements HttpSessionListener,
     }
 
     ApplicationContext getContext() {
-        Assert.notNull(context, "setContext(...) never called, ApplicationContext must not be null");
+       Assert.notNull(context, "setContext(...) never called, ApplicationContext must not be null");
         return context;
     }
 }

+ 458 - 452
core/src/test/java/org/acegisecurity/captcha/CaptchaChannelProcessorTests.java

@@ -15,532 +15,538 @@
 
 package net.sf.acegisecurity.captcha;
 
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-
 import junit.framework.TestCase;
 import net.sf.acegisecurity.ConfigAttributeDefinition;
 import net.sf.acegisecurity.MockFilterChain;
 import net.sf.acegisecurity.SecurityConfig;
 import net.sf.acegisecurity.context.SecurityContextHolder;
 import net.sf.acegisecurity.intercept.web.FilterInvocation;
-
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
 
-/** 
- * Tests {@link CaptchaChannelProcessor} 
+import javax.servlet.ServletException;
+import java.io.IOException;
+
+/**
+ * Tests {@link CaptchaChannelProcessor}
+ *
  * @author marc antoine Garrigue
  * @version $Id$
  */
 public class CaptchaChannelProcessorTests extends TestCase {
 
-	public void testDecideRequestsFirstTestRequests() throws Exception {
-		ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
-		cad.addConfigAttribute(new SecurityConfig("SOME_IGNORED_ATTRIBUTE"));
-		cad.addConfigAttribute(new SecurityConfig(
-				"REQUIRES_HUMAN_AFTER_MAX_REQUESTS"));
+    public void testDecideRequestsFirstTestRequests() throws Exception {
+        ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
+        cad.addConfigAttribute(new SecurityConfig("SOME_IGNORED_ATTRIBUTE"));
+        cad.addConfigAttribute(new SecurityConfig(
+                "REQUIRES_HUMAN_AFTER_MAX_REQUESTS"));
+
+        CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
+        SecurityContextHolder.setContext(context);
+
+        CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
+        CaptchaEntryPoint epoint = new CaptchaEntryPoint();
+        epoint.setCaptchaFormUrl("/jcaptcha.do");
+        epoint.setIncludeOriginalRequest(false);
+
+        processor.setEntryPoint(epoint);
+
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        request.setQueryString("info=true");
+        request.setServerName("localhost");
+        request.setContextPath("/demo");
+        request.setServletPath("/restricted");
+        request.setScheme("http");
+        request.setServerPort(8000);
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockFilterChain chain = new MockFilterChain();
+        FilterInvocation fi = new FilterInvocation(request, response, chain);
+
+        processor.decide(fi, cad);
+        assertEquals(response.getRedirectedUrl(),
+                "http://localhost:8000/demo/jcaptcha.do");
+
+        processor.setMaxRequestsBeforeFirstTest(1);
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(response.getRedirectedUrl(), null);
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(response.getRedirectedUrl(),
+                "http://localhost:8000/demo/jcaptcha.do");
+
+        processor.setMaxRequestsBeforeFirstTest(2);
+        processor.setMaxMillisBeforeReTest(0);
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(null, response.getRedirectedUrl());
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
+    }
+
+    public void testDecideRequestsFirstTestMillis() throws Exception {
+        ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
+        cad.addConfigAttribute(new SecurityConfig("SOME_IGNORED_ATTRIBUTE"));
+        cad.addConfigAttribute(new SecurityConfig(
+                "REQUIRES_HUMAN_AFTER_MAX_MILLIS"));
+
+        CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
+        SecurityContextHolder.setContext(context);
+
+        CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
+        CaptchaEntryPoint epoint = new CaptchaEntryPoint();
+        epoint.setCaptchaFormUrl("/jcaptcha.do");
+        epoint.setIncludeOriginalRequest(false);
+
+        processor.setEntryPoint(epoint);
+
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        request.setQueryString("info=true");
+        request.setServerName("localhost");
+        request.setContextPath("/demo");
+        request.setServletPath("/restricted");
+        request.setScheme("http");
+        request.setServerPort(8000);
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockFilterChain chain = new MockFilterChain();
+        FilterInvocation fi = new FilterInvocation(request, response, chain);
+
+        processor.decide(fi, cad);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
+
+        processor.setMaxRequestsBeforeFirstTest(1);
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(null, response.getRedirectedUrl());
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
+
+        processor.setMaxRequestsBeforeFirstTest(2);
+        processor.setMaxRequestsBeforeReTest(0);
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(null, response.getRedirectedUrl());
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
+
+    }
+
+    public void testDecideRequestsReTest() throws Exception {
+        ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
+        cad.addConfigAttribute(new SecurityConfig("SOME_IGNORED_ATTRIBUTE"));
+        cad.addConfigAttribute(new SecurityConfig(
+                "REQUIRES_HUMAN_AFTER_MAX_REQUESTS"));
+
+        CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
+        SecurityContextHolder.setContext(context);
+
+        CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
+        CaptchaEntryPoint epoint = new CaptchaEntryPoint();
+        epoint.setCaptchaFormUrl("/jcaptcha.do");
+        epoint.setIncludeOriginalRequest(false);
+
+        processor.setEntryPoint(epoint);
+
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        request.setQueryString("info=true");
+        request.setServerName("localhost");
+        request.setContextPath("/demo");
+        request.setServletPath("/restricted");
+        request.setScheme("http");
+        request.setServerPort(8000);
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockFilterChain chain = new MockFilterChain();
+        FilterInvocation fi = new FilterInvocation(request, response, chain);
+
+        processor.decide(fi, cad);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
+
+        processor.setMaxRequestsBeforeFirstTest(1);
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(response.getRedirectedUrl(), null);
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
+
+        processor.setMaxRequestsBeforeReTest(2);
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
+
+        context.setHuman();
+        SecurityContextHolder.setContext(context);
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(null, response.getRedirectedUrl());
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(null, response.getRedirectedUrl());
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
+
+        processor.setMaxMillisBeforeReTest(0);
+        context.setHuman();
+        SecurityContextHolder.setContext(context);
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(null, response.getRedirectedUrl());
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(null, response.getRedirectedUrl());
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
+
+        context.setHuman();
+        SecurityContextHolder.setContext(context);
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(null, response.getRedirectedUrl());
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(null, response.getRedirectedUrl());
+
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
+    }
+
+    private MockHttpServletResponse decideWithNewResponse(
+            ConfigAttributeDefinition cad, CaptchaChannelProcessor processor,
+            MockHttpServletRequest request) throws IOException,
+            ServletException {
+        MockHttpServletResponse response;
+        MockFilterChain chain;
+        FilterInvocation fi;
+        response = new MockHttpServletResponse();
+        chain = new MockFilterChain();
+        fi = new FilterInvocation(request, response, chain);
+        processor.decide(fi, cad);
+        return response;
+    }
+
+    public void testDecideRejectsNulls() throws Exception {
+        CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
+        processor.setEntryPoint(new CaptchaEntryPoint());
+        processor.afterPropertiesSet();
+
+        try {
+            processor.decide(null, null);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testDecideMillis() throws Exception {
+        ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
+        cad.addConfigAttribute(new SecurityConfig("SOME_IGNORED_ATTRIBUTE"));
+        cad.addConfigAttribute(new SecurityConfig(
+                "REQUIRES_HUMAN_AFTER_MAX_MILLIS"));
+
+        CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
+        SecurityContextHolder.setContext(context);
+
+        CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
+        CaptchaEntryPoint epoint = new CaptchaEntryPoint();
+        epoint.setCaptchaFormUrl("/jcaptcha.do");
+        epoint.setIncludeOriginalRequest(false);
+
+        processor.setEntryPoint(epoint);
+
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        request.setQueryString("info=true");
+        request.setServerName("localhost");
+        request.setContextPath("/demo");
+        request.setServletPath("/restricted");
+        request.setScheme("http");
+        request.setServerPort(8000);
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockFilterChain chain = new MockFilterChain();
+        FilterInvocation fi = new FilterInvocation(request, response, chain);
+
+        processor.decide(fi, cad);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
+
+        processor.setMaxRequestsBeforeFirstTest(1);
 
-		CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
-		SecurityContextHolder.setContext(context);
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(response.getRedirectedUrl(), null);
 
-		CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
-		CaptchaEntryPoint epoint = new CaptchaEntryPoint();
-		epoint.setCaptchaFormUrl("/jcaptcha.do");
-		processor.setEntryPoint(epoint);
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
 
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request.setQueryString("info=true");
-		request.setServerName("localhost");
-		request.setContextPath("/demo");
-		request.setServletPath("/restricted");
-		request.setScheme("http");
-		request.setServerPort(8000);
-
-		MockHttpServletResponse response = new MockHttpServletResponse();
-		MockFilterChain chain = new MockFilterChain();
-		FilterInvocation fi = new FilterInvocation(request, response, chain);
-
-		processor.decide(fi, cad);
-		assertEquals(response.getRedirectedUrl(),
-				"http://localhost:8000/demo/jcaptcha.do");
-
-		processor.setMaxRequestsBeforeFirstTest(1);
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(response.getRedirectedUrl(), null);
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(response.getRedirectedUrl(),
-				"http://localhost:8000/demo/jcaptcha.do");
-
-		processor.setMaxRequestsBeforeFirstTest(2);
-		processor.setMaxMillisBeforeReTest(0);
+        processor.setMaxMillisBeforeReTest(100);
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(null, response.getRedirectedUrl());
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
-	}
+        context.setHuman();
+        SecurityContextHolder.setContext(context);
 
-	public void testDecideRequestsFirstTestMillis() throws Exception {
-		ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
-		cad.addConfigAttribute(new SecurityConfig("SOME_IGNORED_ATTRIBUTE"));
-		cad.addConfigAttribute(new SecurityConfig(
-				"REQUIRES_HUMAN_AFTER_MAX_MILLIS"));
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(null, response.getRedirectedUrl());
 
-		CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
-		SecurityContextHolder.setContext(context);
+       waitFor(100);
 
-		CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
-		CaptchaEntryPoint epoint = new CaptchaEntryPoint();
-		epoint.setCaptchaFormUrl("/jcaptcha.do");
-		processor.setEntryPoint(epoint);
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
 
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request.setQueryString("info=true");
-		request.setServerName("localhost");
-		request.setContextPath("/demo");
-		request.setServletPath("/restricted");
-		request.setScheme("http");
-		request.setServerPort(8000);
+        processor.setMaxRequestsBeforeReTest(0);
+        context.setHuman();
+        SecurityContextHolder.setContext(context);
 
-		MockHttpServletResponse response = new MockHttpServletResponse();
-		MockFilterChain chain = new MockFilterChain();
-		FilterInvocation fi = new FilterInvocation(request, response, chain);
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(null, response.getRedirectedUrl());
 
-		processor.decide(fi, cad);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(null, response.getRedirectedUrl());
 
-		processor.setMaxRequestsBeforeFirstTest(1);
+       waitFor(100);
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(null, response.getRedirectedUrl());
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
+        context.setHuman();
+        SecurityContextHolder.setContext(context);
 
-		processor.setMaxRequestsBeforeFirstTest(2);
-		processor.setMaxRequestsBeforeReTest(0);
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(null, response.getRedirectedUrl());
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(null, response.getRedirectedUrl());
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(null, response.getRedirectedUrl());
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
-
-	}
-
-	public void testDecideRequestsReTest() throws Exception {
-		ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
-		cad.addConfigAttribute(new SecurityConfig("SOME_IGNORED_ATTRIBUTE"));
-		cad.addConfigAttribute(new SecurityConfig(
-				"REQUIRES_HUMAN_AFTER_MAX_REQUESTS"));
-
-		CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
-		SecurityContextHolder.setContext(context);
-
-		CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
-		CaptchaEntryPoint epoint = new CaptchaEntryPoint();
-		epoint.setCaptchaFormUrl("/jcaptcha.do");
-		processor.setEntryPoint(epoint);
-
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request.setQueryString("info=true");
-		request.setServerName("localhost");
-		request.setContextPath("/demo");
-		request.setServletPath("/restricted");
-		request.setScheme("http");
-		request.setServerPort(8000);
-
-		MockHttpServletResponse response = new MockHttpServletResponse();
-		MockFilterChain chain = new MockFilterChain();
-		FilterInvocation fi = new FilterInvocation(request, response, chain);
-
-		processor.decide(fi, cad);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
-
-		processor.setMaxRequestsBeforeFirstTest(1);
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(response.getRedirectedUrl(), null);
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
-
-		processor.setMaxRequestsBeforeReTest(2);
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
-
-		context.setHuman();
-		SecurityContextHolder.setContext(context);
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(null, response.getRedirectedUrl());
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(null, response.getRedirectedUrl());
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
-
-		processor.setMaxMillisBeforeReTest(0);
-		context.setHuman();
-		SecurityContextHolder.setContext(context);
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(null, response.getRedirectedUrl());
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(null, response.getRedirectedUrl());
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
-
-		context.setHuman();
-		SecurityContextHolder.setContext(context);
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(null, response.getRedirectedUrl());
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(null, response.getRedirectedUrl());
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
-	}
-
-	private MockHttpServletResponse decideWithNewResponse(
-			ConfigAttributeDefinition cad, CaptchaChannelProcessor processor,
-			MockHttpServletRequest request) throws IOException,
-			ServletException {
-		MockHttpServletResponse response;
-		MockFilterChain chain;
-		FilterInvocation fi;
-		response = new MockHttpServletResponse();
-		chain = new MockFilterChain();
-		fi = new FilterInvocation(request, response, chain);
-		processor.decide(fi, cad);
-		return response;
-	}
-
-	public void testDecideRejectsNulls() throws Exception {
-		CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
-		processor.setEntryPoint(new CaptchaEntryPoint());
-		processor.afterPropertiesSet();
-
-		try {
-			processor.decide(null, null);
-			fail("Should have thrown IllegalArgumentException");
-		} catch (IllegalArgumentException expected) {
-			assertTrue(true);
-		}
-	}
-/*
-  
- // TODO: Re-enable these tests.
-  
-   Commented out by Ben Alex on 19 Sep 05 as the Thread.sleep(100) approach to simulating
-   request age caused intermittent problems. An alternative approach should be used
-   instead, such as (a) modifying the CaptchaSecurityContextImpl (why not make a package
-   protected setLastPassedCaptchaDateInMillis) or (b) providing a package protected method
-   so that the unit test can modify the time being used by CaptchaChannelProcesor instead
-   of using System.currentTimeMillis().
-   
-	public void testDecideMillis() throws Exception {
-		ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
-		cad.addConfigAttribute(new SecurityConfig("SOME_IGNORED_ATTRIBUTE"));
-		cad.addConfigAttribute(new SecurityConfig(
-				"REQUIRES_HUMAN_AFTER_MAX_MILLIS"));
-
-		CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
-		SecurityContextHolder.setContext(context);
-
-		CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
-		CaptchaEntryPoint epoint = new CaptchaEntryPoint();
-		epoint.setCaptchaFormUrl("/jcaptcha.do");
-		processor.setEntryPoint(epoint);
-
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request.setQueryString("info=true");
-		request.setServerName("localhost");
-		request.setContextPath("/demo");
-		request.setServletPath("/restricted");
-		request.setScheme("http");
-		request.setServerPort(8000);
-
-		MockHttpServletResponse response = new MockHttpServletResponse();
-		MockFilterChain chain = new MockFilterChain();
-		FilterInvocation fi = new FilterInvocation(request, response, chain);
-
-		processor.decide(fi, cad);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
-
-		processor.setMaxRequestsBeforeFirstTest(1);
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(response.getRedirectedUrl(), null);
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
-
-		processor.setMaxMillisBeforeReTest(100);
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
-
-		context.setHuman();
-		SecurityContextHolder.setContext(context);
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(null, response.getRedirectedUrl());
-
-		Thread.sleep(100);
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
-
-		processor.setMaxRequestsBeforeReTest(0);
-		context.setHuman();
-		SecurityContextHolder.setContext(context);
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(null, response.getRedirectedUrl());
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(null, response.getRedirectedUrl());
-
-		Thread.sleep(100);
-
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
-
-		context.setHuman();
-		SecurityContextHolder.setContext(context);
+       waitFor(100);
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(null, response.getRedirectedUrl());
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
+    }
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(null, response.getRedirectedUrl());
+    public void testDecideBoth() throws Exception {
+        ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
+        cad.addConfigAttribute(new SecurityConfig("SOME_IGNORED_ATTRIBUTE"));
+        cad.addConfigAttribute(new SecurityConfig(
+                "REQUIRES_HUMAN_AFTER_MAX_MILLIS"));
+        cad.addConfigAttribute(new SecurityConfig(
+                "REQUIRES_HUMAN_AFTER_MAX_REQUESTS"));
 
-		Thread.sleep(100);
+        CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
+        SecurityContextHolder.setContext(context);
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
-	}
+        CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
+        CaptchaEntryPoint epoint = new CaptchaEntryPoint();
+        epoint.setCaptchaFormUrl("/jcaptcha.do");
+        epoint.setIncludeOriginalRequest(false);
 
-	public void testDecideBoth() throws Exception {
-		ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
-		cad.addConfigAttribute(new SecurityConfig("SOME_IGNORED_ATTRIBUTE"));
-		cad.addConfigAttribute(new SecurityConfig(
-				"REQUIRES_HUMAN_AFTER_MAX_MILLIS"));
-		cad.addConfigAttribute(new SecurityConfig(
-				"REQUIRES_HUMAN_AFTER_MAX_REQUESTS"));
+        processor.setEntryPoint(epoint);
 
-		CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
-		SecurityContextHolder.setContext(context);
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        request.setQueryString("info=true");
+        request.setServerName("localhost");
+        request.setContextPath("/demo");
+        request.setServletPath("/restricted");
+        request.setScheme("http");
+        request.setServerPort(8000);
 
-		CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
-		CaptchaEntryPoint epoint = new CaptchaEntryPoint();
-		epoint.setCaptchaFormUrl("/jcaptcha.do");
-		processor.setEntryPoint(epoint);
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockFilterChain chain = new MockFilterChain();
+        FilterInvocation fi = new FilterInvocation(request, response, chain);
 
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request.setQueryString("info=true");
-		request.setServerName("localhost");
-		request.setContextPath("/demo");
-		request.setServletPath("/restricted");
-		request.setScheme("http");
-		request.setServerPort(8000);
+        processor.decide(fi, cad);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
 
-		MockHttpServletResponse response = new MockHttpServletResponse();
-		MockFilterChain chain = new MockFilterChain();
-		FilterInvocation fi = new FilterInvocation(request, response, chain);
+        processor.setMaxRequestsBeforeFirstTest(1);
 
-		processor.decide(fi, cad);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(response.getRedirectedUrl(), null);
 
-		processor.setMaxRequestsBeforeFirstTest(1);
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(response.getRedirectedUrl(), null);
+        processor.setMaxMillisBeforeReTest(100);
+        processor.setMaxRequestsBeforeReTest(2);
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
 
-		processor.setMaxMillisBeforeReTest(100);
-		processor.setMaxRequestsBeforeReTest(2);
+        context.setHuman();
+        SecurityContextHolder.setContext(context);
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(null, response.getRedirectedUrl());
 
-		context.setHuman();
-		SecurityContextHolder.setContext(context);
+       waitFor(100);
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(null, response.getRedirectedUrl());
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
 
-		Thread.sleep(100);
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
+        context.setHuman();
+        SecurityContextHolder.setContext(context);
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(null, response.getRedirectedUrl());
 
-		context.setHuman();
-		SecurityContextHolder.setContext(context);
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(null, response.getRedirectedUrl());
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(null, response.getRedirectedUrl());
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(null, response.getRedirectedUrl());
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
+        context.setHuman();
+        SecurityContextHolder.setContext(context);
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals(null, response.getRedirectedUrl());
 
-		context.setHuman();
-		SecurityContextHolder.setContext(context);
+       waitFor(100);
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals(null, response.getRedirectedUrl());
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
 
-		Thread.sleep(100);
+        response = decideWithNewResponse(cad, processor, request);
+        assertEquals("http://localhost:8000/demo/jcaptcha.do", response
+                .getRedirectedUrl());
+    }
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
+    public void testGettersSetters() {
+        CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
+        assertEquals("REQUIRES_HUMAN_AFTER_MAX_MILLIS", processor
+                .getRequiresHumanAfterMaxMillisKeyword());
+        processor.setRequiresHumanAfterMaxMillisKeyword("X");
+        assertEquals("X", processor.getRequiresHumanAfterMaxMillisKeyword());
 
-		response = decideWithNewResponse(cad, processor, request);
-		assertEquals("http://localhost:8000/demo/jcaptcha.do", response
-				.getRedirectedUrl());
-	}
-*/
-	public void testGettersSetters() {
-		CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
-		assertEquals("REQUIRES_HUMAN_AFTER_MAX_MILLIS", processor
-				.getRequiresHumanAfterMaxMillisKeyword());
-		processor.setRequiresHumanAfterMaxMillisKeyword("X");
-		assertEquals("X", processor.getRequiresHumanAfterMaxMillisKeyword());
+        assertEquals("REQUIRES_HUMAN_AFTER_MAX_REQUESTS", processor
+                .getRequiresHumanAfterMaxRequestsKeyword());
+        processor.setRequiresHumanAfterMaxRequestsKeyword("Y");
+        assertEquals("Y", processor.getRequiresHumanAfterMaxRequestsKeyword());
 
-		assertEquals("REQUIRES_HUMAN_AFTER_MAX_REQUESTS", processor
-				.getRequiresHumanAfterMaxRequestsKeyword());
-		processor.setRequiresHumanAfterMaxRequestsKeyword("Y");
-		assertEquals("Y", processor.getRequiresHumanAfterMaxRequestsKeyword());
+        assertEquals(0, processor.getMaxRequestsBeforeFirstTest());
+        processor.setMaxRequestsBeforeFirstTest(1);
+        assertEquals(1, processor.getMaxRequestsBeforeFirstTest());
 
-		assertEquals(0, processor.getMaxRequestsBeforeFirstTest());
-		processor.setMaxRequestsBeforeFirstTest(1);
-		assertEquals(1, processor.getMaxRequestsBeforeFirstTest());
+        assertEquals(-1, processor.getMaxRequestsBeforeReTest());
+        processor.setMaxRequestsBeforeReTest(11);
+        assertEquals(11, processor.getMaxRequestsBeforeReTest());
 
-		assertEquals(-1, processor.getMaxRequestsBeforeReTest());
-		processor.setMaxRequestsBeforeReTest(11);
-		assertEquals(11, processor.getMaxRequestsBeforeReTest());
+        assertEquals(-1, processor.getMaxMillisBeforeReTest());
+        processor.setMaxMillisBeforeReTest(111);
+        assertEquals(111, processor.getMaxMillisBeforeReTest());
 
-		assertEquals(-1, processor.getMaxMillisBeforeReTest());
-		processor.setMaxMillisBeforeReTest(111);
-		assertEquals(111, processor.getMaxMillisBeforeReTest());
+        assertTrue(processor.getEntryPoint() == null);
+        processor.setEntryPoint(new CaptchaEntryPoint());
+        assertTrue(processor.getEntryPoint() != null);
+    }
 
-		assertTrue(processor.getEntryPoint() == null);
-		processor.setEntryPoint(new CaptchaEntryPoint());
-		assertTrue(processor.getEntryPoint() != null);
-	}
+    public void testMissingEntryPoint() throws Exception {
+        CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
+        processor.setEntryPoint(null);
 
-	public void testMissingEntryPoint() throws Exception {
-		CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
-		processor.setEntryPoint(null);
+        try {
+            processor.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("entryPoint required", expected.getMessage());
+        }
+    }
 
-		try {
-			processor.afterPropertiesSet();
-			fail("Should have thrown IllegalArgumentException");
-		} catch (IllegalArgumentException expected) {
-			assertEquals("entryPoint required", expected.getMessage());
-		}
-	}
+    public void testMissingKeyword() throws Exception {
+        CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
+        processor.setRequiresHumanAfterMaxMillisKeyword(null);
 
-	public void testMissingKeyword() throws Exception {
-		CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
-		processor.setRequiresHumanAfterMaxMillisKeyword(null);
+        try {
+            processor.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
 
-		try {
-			processor.afterPropertiesSet();
-			fail("Should have thrown IllegalArgumentException");
-		} catch (IllegalArgumentException expected) {
+        }
+        processor.setRequiresHumanAfterMaxMillisKeyword("");
 
-		}
-		processor.setRequiresHumanAfterMaxMillisKeyword("");
+        try {
+            processor.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
 
-		try {
-			processor.afterPropertiesSet();
-			fail("Should have thrown IllegalArgumentException");
-		} catch (IllegalArgumentException expected) {
+        }
+        processor.setRequiresHumanAfterMaxRequestsKeyword("");
 
-		}
-		processor.setRequiresHumanAfterMaxRequestsKeyword("");
+        try {
+            processor.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
 
-		try {
-			processor.afterPropertiesSet();
-			fail("Should have thrown IllegalArgumentException");
-		} catch (IllegalArgumentException expected) {
+        }
 
-		}
-
-		processor.setRequiresHumanAfterMaxRequestsKeyword(null);
-
-		try {
-			processor.afterPropertiesSet();
-			fail("Should have thrown IllegalArgumentException");
-		} catch (IllegalArgumentException expected) {
-
-		}
-
-	}
-
-	public void testSupports() {
-		CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
-		assertTrue(processor.supports(new SecurityConfig(processor
-				.getRequiresHumanAfterMaxMillisKeyword())));
-		assertTrue(processor.supports(new SecurityConfig(processor
-				.getRequiresHumanAfterMaxRequestsKeyword())));
-
-		assertTrue(processor.supports(new SecurityConfig(
-				"REQUIRES_HUMAN_AFTER_MAX_REQUESTS")));
-		assertTrue(processor.supports(new SecurityConfig(
-				"REQUIRES_HUMAN_AFTER_MAX_MILLIS")));
-
-		assertFalse(processor.supports(null));
-
-		assertFalse(processor.supports(new SecurityConfig("NOT_SUPPORTED")));
-	}
+        processor.setRequiresHumanAfterMaxRequestsKeyword(null);
+
+        try {
+            processor.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+
+        }
+
+    }
+
+    public void testSupports() {
+        CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
+        assertTrue(processor.supports(new SecurityConfig(processor
+                .getRequiresHumanAfterMaxMillisKeyword())));
+        assertTrue(processor.supports(new SecurityConfig(processor
+                .getRequiresHumanAfterMaxRequestsKeyword())));
+
+        assertTrue(processor.supports(new SecurityConfig(
+                "REQUIRES_HUMAN_AFTER_MAX_REQUESTS")));
+        assertTrue(processor.supports(new SecurityConfig(
+                "REQUIRES_HUMAN_AFTER_MAX_MILLIS")));
+
+        assertFalse(processor.supports(null));
+
+        assertFalse(processor.supports(new SecurityConfig("NOT_SUPPORTED")));
+    }
+
+    private void waitFor(int time){
+        long start=System.currentTimeMillis();
+        while(System.currentTimeMillis()<start+time){
+        }
+        return;
+    }
 
 }

+ 402 - 357
core/src/test/java/org/acegisecurity/captcha/CaptchaEntryPointTests.java

@@ -15,371 +15,416 @@
 
 package net.sf.acegisecurity.captcha;
 
-import java.util.HashMap;
-import java.util.Map;
-
 import junit.framework.TestCase;
 import net.sf.acegisecurity.MockPortResolver;
-import net.sf.acegisecurity.securechannel.RetryWithHttpEntryPoint;
 import net.sf.acegisecurity.util.PortMapperImpl;
-
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
 
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Map;
+
 /**
- * Tests {@link RetryWithHttpEntryPoint}.
- * 
- * @author Ben Alex
- * @version $Id: RetryWithHttpEntryPointTests.java,v 1.4 2005/04/11 01:07:02
- *          luke_t Exp $
+ * Tests {@link CaptchaEntryPoint}.
+ *
+ * @author marc antoine Garrigue
+ * @version $Id$
  */
 public class CaptchaEntryPointTests extends TestCase {
-	// ~ Methods
-	// ================================================================
-
-	public final void setUp() throws Exception {
-		super.setUp();
-	}
-
-	public static void main(String[] args) {
-		junit.textui.TestRunner.run(CaptchaEntryPointTests.class);
-	}
-
-	public void testDetectsMissingCaptchaFormUrl() throws Exception {
-		CaptchaEntryPoint ep = new CaptchaEntryPoint();
-		ep.setPortMapper(new PortMapperImpl());
-		ep.setPortResolver(new MockPortResolver(80, 443));
-
-		try {
-			ep.afterPropertiesSet();
-			fail("Should have thrown IllegalArgumentException");
-		} catch (IllegalArgumentException expected) {
-			assertEquals("captchaFormUrl must be specified", expected
-					.getMessage());
-		}
-	}
-
-	public void testDetectsMissingPortMapper() throws Exception {
-		CaptchaEntryPoint ep = new CaptchaEntryPoint();
-		ep.setCaptchaFormUrl("xxx");
-		ep.setPortMapper(null);
-
-		try {
-			ep.afterPropertiesSet();
-			fail("Should have thrown IllegalArgumentException");
-		} catch (IllegalArgumentException expected) {
-			assertEquals("portMapper must be specified", expected.getMessage());
-		}
-	}
-
-	public void testDetectsMissingPortResolver() throws Exception {
-		CaptchaEntryPoint ep = new CaptchaEntryPoint();
-		ep.setCaptchaFormUrl("xxx");
-		ep.setPortResolver(null);
-
-		try {
-			ep.afterPropertiesSet();
-			fail("Should have thrown IllegalArgumentException");
-		} catch (IllegalArgumentException expected) {
-			assertEquals("portResolver must be specified", expected
-					.getMessage());
-		}
-
-	}
-
-	public void testGettersSetters() {
-		CaptchaEntryPoint ep = new CaptchaEntryPoint();
-		ep.setCaptchaFormUrl("/hello");
-		ep.setPortMapper(new PortMapperImpl());
-		ep.setPortResolver(new MockPortResolver(8080, 8443));
-		assertEquals("/hello", ep.getCaptchaFormUrl());
-		assertTrue(ep.getPortMapper() != null);
-		assertTrue(ep.getPortResolver() != null);
-
-		assertEquals("originalRequest", ep.getOriginalRequestParameterName());
-		ep.setOriginalRequestParameterName("Z");
-		assertEquals("Z", ep.getOriginalRequestParameterName());
-
-		assertEquals(false, ep.isIncludeOriginalRequest());
-		ep.setIncludeOriginalRequest(true);
-		assertEquals(true, ep.isIncludeOriginalRequest());
-
-		assertEquals(false, ep.isOutsideWebApp());
-		ep.setOutsideWebApp(true);
-		assertEquals(true, ep.isOutsideWebApp());
-
-		ep.setForceHttps(false);
-		assertFalse(ep.getForceHttps());
-		ep.setForceHttps(true);
-		assertTrue(ep.getForceHttps());
-
-	}
-
-	public void testHttpsOperationFromOriginalHttpUrl() throws Exception {
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request.setRequestURI("/some_path");
-		request.setScheme("http");
-		request.setServerName("www.example.com");
-		request.setContextPath("/bigWebApp");
-		request.setServerPort(80);
-
-		MockHttpServletResponse response = new MockHttpServletResponse();
-
-		CaptchaEntryPoint ep = new CaptchaEntryPoint();
-		ep.setCaptchaFormUrl("/hello");
-		ep.setPortMapper(new PortMapperImpl());
-		ep.setForceHttps(true);
-		ep.setPortMapper(new PortMapperImpl());
-		ep.setPortResolver(new MockPortResolver(80, 443));
-		ep.afterPropertiesSet();
-
-		ep.commence(request, response);
-		assertEquals("https://www.example.com/bigWebApp/hello", response
-				.getRedirectedUrl());
-
-		request.setServerPort(8080);
-		response = new MockHttpServletResponse();
-		ep.setPortResolver(new MockPortResolver(8080, 8443));
-		ep.commence(request, response);
-		assertEquals("https://www.example.com:8443/bigWebApp/hello", response
-				.getRedirectedUrl());
-
-		// Now test an unusual custom HTTP:HTTPS is handled properly
-		request.setServerPort(8888);
-		response = new MockHttpServletResponse();
-		ep.commence(request, response);
-		assertEquals("https://www.example.com:8443/bigWebApp/hello", response
-				.getRedirectedUrl());
-
-		PortMapperImpl portMapper = new PortMapperImpl();
-		Map map = new HashMap();
-		map.put("8888", "9999");
-		portMapper.setPortMappings(map);
-		response = new MockHttpServletResponse();
-
-		ep = new CaptchaEntryPoint();
-		ep.setCaptchaFormUrl("/hello");
-		ep.setPortMapper(new PortMapperImpl());
-		ep.setForceHttps(true);
-		ep.setPortMapper(portMapper);
-		ep.setPortResolver(new MockPortResolver(8888, 9999));
-		ep.afterPropertiesSet();
-
-		ep.commence(request, response);
-		assertEquals("https://www.example.com:9999/bigWebApp/hello", response
-				.getRedirectedUrl());
-	}
-
-	public void testHttpsOperationFromOriginalHttpsUrl() throws Exception {
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request.setRequestURI("/some_path");
-		request.setScheme("https");
-		request.setServerName("www.example.com");
-		request.setContextPath("/bigWebApp");
-		request.setServerPort(443);
-
-		MockHttpServletResponse response = new MockHttpServletResponse();
-
-		CaptchaEntryPoint ep = new CaptchaEntryPoint();
-		ep.setCaptchaFormUrl("/hello");
-		ep.setPortMapper(new PortMapperImpl());
-		ep.setForceHttps(true);
-		ep.setPortMapper(new PortMapperImpl());
-		ep.setPortResolver(new MockPortResolver(80, 443));
-		ep.afterPropertiesSet();
-
-		ep.commence(request, response);
-		assertEquals("https://www.example.com/bigWebApp/hello", response
-				.getRedirectedUrl());
-
-		request.setServerPort(8443);
-		response = new MockHttpServletResponse();
-		ep.setPortResolver(new MockPortResolver(8080, 8443));
-		ep.commence(request, response);
-		assertEquals("https://www.example.com:8443/bigWebApp/hello", response
-				.getRedirectedUrl());
-	}
-
-	public void testNormalOperation() throws Exception {
-		CaptchaEntryPoint ep = new CaptchaEntryPoint();
-		ep.setCaptchaFormUrl("/hello");
-		ep.setPortMapper(new PortMapperImpl());
-		ep.setPortResolver(new MockPortResolver(80, 443));
-		ep.afterPropertiesSet();
-
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request.setRequestURI("/some_path");
-		request.setContextPath("/bigWebApp");
-		request.setScheme("http");
-		request.setServerName("www.example.com");
-		request.setContextPath("/bigWebApp");
-		request.setServerPort(80);
-
-		MockHttpServletResponse response = new MockHttpServletResponse();
-
-		ep.afterPropertiesSet();
-		ep.commence(request, response);
-		assertEquals("http://www.example.com/bigWebApp/hello", response
-				.getRedirectedUrl());
-	}
-
-	public void testOperationWhenHttpsRequestsButHttpsPortUnknown()
-			throws Exception {
-		CaptchaEntryPoint ep = new CaptchaEntryPoint();
-		ep.setCaptchaFormUrl("/hello");
-		ep.setPortMapper(new PortMapperImpl());
-		ep.setPortResolver(new MockPortResolver(8888, 1234));
-		ep.setForceHttps(true);
-		ep.afterPropertiesSet();
-
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request.setRequestURI("/some_path");
-		request.setContextPath("/bigWebApp");
-		request.setScheme("http");
-		request.setServerName("www.example.com");
-		request.setContextPath("/bigWebApp");
-		request.setServerPort(8888); // NB: Port we can't resolve
-
-		MockHttpServletResponse response = new MockHttpServletResponse();
-
-		ep.afterPropertiesSet();
-		ep.commence(request, response);
-
-		// Response doesn't switch to HTTPS, as we didn't know HTTP port 8888 to
-		// HTTP port mapping
-		assertEquals("http://www.example.com:8888/bigWebApp/hello", response
-				.getRedirectedUrl());
-	}
-
-	public void testOperationWithOriginalRequestIncludes() throws Exception {
-		CaptchaEntryPoint ep = new CaptchaEntryPoint();
-		ep.setCaptchaFormUrl("/hello");
-		PortMapperImpl mapper = new PortMapperImpl();
-		mapper.getTranslatedPortMappings().put(new Integer(8888),
-				new Integer(1234));
-		ep.setPortMapper(mapper);
-
-		ep.setPortResolver(new MockPortResolver(8888, 1234));
-		ep.setIncludeOriginalRequest(true);
-		ep.afterPropertiesSet();
-
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request.setRequestURI("/some_path");
-		request.setScheme("http");
-		request.setServerName("www.example.com");
-		// request.setContextPath("/bigWebApp");
-		// TODO correct this when the getRequestUrl from mock works...
-
-		request.setServerPort(8888); // NB: Port we can't resolve
-
-		MockHttpServletResponse response = new MockHttpServletResponse();
-
-		ep.afterPropertiesSet();
-		ep.commence(request, response);
-		assertEquals(
-				"http://www.example.com:8888/hello?originalRequest=http://www.example.com:8888/some_path",
-				response.getRedirectedUrl());
-
-		// test the query params
-		request.addParameter("name", "value");
-		response = new MockHttpServletResponse();
-		ep.commence(request, response);
-		assertEquals(
-				"http://www.example.com:8888/hello?originalRequest=http://www.example.com:8888/some_path?name=value",
-				response.getRedirectedUrl());
-
-		// test the multiple query params
-		request.addParameter("name", "value");
-		request.addParameter("name1", "value2");
-		response = new MockHttpServletResponse();
-		ep.commence(request, response);
-		assertEquals(
-				"http://www.example.com:8888/hello?originalRequest=http://www.example.com:8888/some_path?name=value&name1=value2",
-				response.getRedirectedUrl());
-
-		// test add parameter to captcha form url??
-
-		ep.setCaptchaFormUrl("/hello?toto=titi");
-		response = new MockHttpServletResponse();
-		ep.commence(request, response);
-		assertEquals(
-				"http://www.example.com:8888/hello?toto=titi&originalRequest=http://www.example.com:8888/some_path?name=value&name1=value2",
-				response.getRedirectedUrl());
-
-		// with forcing!!!
-		ep.setForceHttps(true);
-		response = new MockHttpServletResponse();
-		ep.commence(request, response);
-		assertEquals(
-				"https://www.example.com:1234/hello?toto=titi&originalRequest=http://www.example.com:8888/some_path?name=value&name1=value2",
-				response.getRedirectedUrl());
-
-	}
-
-	public void testOperationWithOutsideWebApp() throws Exception {
-		CaptchaEntryPoint ep = new CaptchaEntryPoint();
-		ep.setCaptchaFormUrl("https://www.jcaptcha.net/dotest/");
-		PortMapperImpl mapper = new PortMapperImpl();
-		mapper.getTranslatedPortMappings().put(new Integer(8888),
-				new Integer(1234));
-		ep.setPortMapper(mapper);
-
-		ep.setPortResolver(new MockPortResolver(8888, 1234));
-		ep.setIncludeOriginalRequest(true);
-		ep.setOutsideWebApp(true);
-
-		ep.afterPropertiesSet();
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request.setRequestURI("/some_path");
-		request.setScheme("http");
-		request.setServerName("www.example.com");
-		// request.setContextPath("/bigWebApp");
-		// TODO correct this when the getRequestUrl from mock works...
-
-		request.setServerPort(8888); // NB: Port we can't resolve
-
-		MockHttpServletResponse response = new MockHttpServletResponse();
-
-		ep.afterPropertiesSet();
-		ep.commence(request, response);
-		assertEquals(
-				"https://www.jcaptcha.net/dotest/?originalRequest=http://www.example.com:8888/some_path",
-				response.getRedirectedUrl());
-
-		// test the query params
-		request.addParameter("name", "value");
-		response = new MockHttpServletResponse();
-		ep.commence(request, response);
-		assertEquals(
-				"https://www.jcaptcha.net/dotest/?originalRequest=http://www.example.com:8888/some_path?name=value",
-				response.getRedirectedUrl());
-
-		// test the multiple query params
-		request.addParameter("name", "value");
-		request.addParameter("name1", "value2");
-		response = new MockHttpServletResponse();
-		ep.commence(request, response);
-		assertEquals(
-				"https://www.jcaptcha.net/dotest/?originalRequest=http://www.example.com:8888/some_path?name=value&name1=value2",
-				response.getRedirectedUrl());
-
-		// test add parameter to captcha form url??
-
-		ep.setCaptchaFormUrl("https://www.jcaptcha.net/dotest/?toto=titi");
-		response = new MockHttpServletResponse();
-		ep.commence(request, response);
-		assertEquals(
-				"https://www.jcaptcha.net/dotest/?toto=titi&originalRequest=http://www.example.com:8888/some_path?name=value&name1=value2",
-				response.getRedirectedUrl());
-
-		// with forcing!!!
-		ep.setForceHttps(true);
-		response = new MockHttpServletResponse();
-		ep.commence(request, response);
-		assertEquals(
-				"https://www.jcaptcha.net/dotest/?toto=titi&originalRequest=http://www.example.com:8888/some_path?name=value&name1=value2",
-				response.getRedirectedUrl());
-
-	}
+    // ~ Methods
+    // ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(CaptchaEntryPointTests.class);
+    }
+
+    public void testDetectsMissingCaptchaFormUrl() throws Exception {
+        CaptchaEntryPoint ep = new CaptchaEntryPoint();
+        ep.setPortMapper(new PortMapperImpl());
+        ep.setPortResolver(new MockPortResolver(80, 443));
+
+        try {
+            ep.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("captchaFormUrl must be specified", expected
+                    .getMessage());
+        }
+    }
+
+    public void testDetectsMissingPortMapper() throws Exception {
+        CaptchaEntryPoint ep = new CaptchaEntryPoint();
+        ep.setCaptchaFormUrl("xxx");
+        ep.setPortMapper(null);
+
+        try {
+            ep.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("portMapper must be specified", expected.getMessage());
+        }
+    }
+
+    public void testDetectsMissingPortResolver() throws Exception {
+        CaptchaEntryPoint ep = new CaptchaEntryPoint();
+        ep.setCaptchaFormUrl("xxx");
+        ep.setPortResolver(null);
+
+        try {
+            ep.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("portResolver must be specified", expected
+                    .getMessage());
+        }
+
+    }
+
+    public void testGettersSetters() {
+        CaptchaEntryPoint ep = new CaptchaEntryPoint();
+        ep.setCaptchaFormUrl("/hello");
+        ep.setPortMapper(new PortMapperImpl());
+        ep.setPortResolver(new MockPortResolver(8080, 8443));
+        assertEquals("/hello", ep.getCaptchaFormUrl());
+        assertTrue(ep.getPortMapper() != null);
+        assertTrue(ep.getPortResolver() != null);
+
+        assertEquals("original_requestUrl", ep.getOriginalRequestUrlParameterName());
+        ep.setOriginalRequestUrlParameterName("Z");
+        assertEquals("Z", ep.getOriginalRequestUrlParameterName());
+
+        assertEquals(true, ep.isIncludeOriginalRequest());
+        ep.setIncludeOriginalRequest(false);
+        assertEquals(false, ep.isIncludeOriginalRequest());
+
+        assertEquals(false, ep.isOutsideWebApp());
+        ep.setOutsideWebApp(true);
+        assertEquals(true, ep.isOutsideWebApp());
+
+        ep.setForceHttps(false);
+        assertFalse(ep.getForceHttps());
+        ep.setForceHttps(true);
+        assertTrue(ep.getForceHttps());
+
+    }
+
+    public void testHttpsOperationFromOriginalHttpUrl() throws Exception {
+        MockHttpServletRequest request = new MockHttpServletRequest();
+
+        request.setRequestURI("/some_path");
+        request.setScheme("http");
+        request.setServerName("www.example.com");
+        request.setContextPath("/bigWebApp");
+        request.setServerPort(80);
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        CaptchaEntryPoint ep = new CaptchaEntryPoint();
+        ep.setIncludeOriginalRequest(false);
+        ep.setCaptchaFormUrl("/hello");
+        ep.setPortMapper(new PortMapperImpl());
+        ep.setForceHttps(true);
+        ep.setPortMapper(new PortMapperImpl());
+        ep.setPortResolver(new MockPortResolver(80, 443));
+        ep.afterPropertiesSet();
+
+        ep.commence(request, response);
+        assertEquals("https://www.example.com/bigWebApp/hello", response
+                .getRedirectedUrl());
+
+        request.setServerPort(8080);
+        response = new MockHttpServletResponse();
+        ep.setPortResolver(new MockPortResolver(8080, 8443));
+        ep.commence(request, response);
+        assertEquals("https://www.example.com:8443/bigWebApp/hello", response
+                .getRedirectedUrl());
+
+        // Now test an unusual custom HTTP:HTTPS is handled properly
+        request.setServerPort(8888);
+        response = new MockHttpServletResponse();
+        ep.commence(request, response);
+        assertEquals("https://www.example.com:8443/bigWebApp/hello", response
+                .getRedirectedUrl());
+
+        PortMapperImpl portMapper = new PortMapperImpl();
+        Map map = new HashMap();
+        map.put("8888", "9999");
+        portMapper.setPortMappings(map);
+        response = new MockHttpServletResponse();
+
+        ep = new CaptchaEntryPoint();
+        ep.setCaptchaFormUrl("/hello");
+        ep.setPortMapper(new PortMapperImpl());
+        ep.setForceHttps(true);
+        ep.setPortMapper(portMapper);
+        ep.setPortResolver(new MockPortResolver(8888, 9999));
+        ep.setIncludeOriginalRequest(false);
+
+        ep.afterPropertiesSet();
+
+        ep.commence(request, response);
+        assertEquals("https://www.example.com:9999/bigWebApp/hello", response
+                .getRedirectedUrl());
+    }
+
+    public void testHttpsOperationFromOriginalHttpsUrl() throws Exception {
+        MockHttpServletRequest request = new MockHttpServletRequest();
+
+        request.setRequestURI("/some_path");
+        request.setScheme("https");
+        request.setServerName("www.example.com");
+        request.setContextPath("/bigWebApp");
+        request.setServerPort(443);
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        CaptchaEntryPoint ep = new CaptchaEntryPoint();
+        ep.setIncludeOriginalRequest(false);
+        ep.setCaptchaFormUrl("/hello");
+        ep.setPortMapper(new PortMapperImpl());
+        ep.setForceHttps(true);
+        ep.setPortMapper(new PortMapperImpl());
+        ep.setPortResolver(new MockPortResolver(80, 443));
+        ep.afterPropertiesSet();
+
+        ep.commence(request, response);
+        assertEquals("https://www.example.com/bigWebApp/hello", response
+                .getRedirectedUrl());
+
+        request.setServerPort(8443);
+        response = new MockHttpServletResponse();
+        ep.setPortResolver(new MockPortResolver(8080, 8443));
+        ep.commence(request, response);
+        assertEquals("https://www.example.com:8443/bigWebApp/hello", response
+                .getRedirectedUrl());
+    }
+
+    public void testNormalOperation() throws Exception {
+        CaptchaEntryPoint ep = new CaptchaEntryPoint();
+        ep.setCaptchaFormUrl("/hello");
+        ep.setPortMapper(new PortMapperImpl());
+        ep.setPortResolver(new MockPortResolver(80, 443));
+        ep.afterPropertiesSet();
+        ep.setIncludeOriginalRequest(false);
+
+
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        request.setRequestURI("/some_path");
+        request.setContextPath("/bigWebApp");
+        request.setScheme("http");
+        request.setServerName("www.example.com");
+        request.setContextPath("/bigWebApp");
+        request.setServerPort(80);
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        ep.afterPropertiesSet();
+        ep.commence(request, response);
+        assertEquals("http://www.example.com/bigWebApp/hello", response
+                .getRedirectedUrl());
+    }
+
+    public void testOperationWhenHttpsRequestsButHttpsPortUnknown()
+            throws Exception {
+        CaptchaEntryPoint ep = new CaptchaEntryPoint();
+        ep.setCaptchaFormUrl("/hello");
+        ep.setPortMapper(new PortMapperImpl());
+        ep.setPortResolver(new MockPortResolver(8888, 1234));
+        ep.setForceHttps(true);
+        ep.setIncludeOriginalRequest(false);
+
+        ep.afterPropertiesSet();
+
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        request.setRequestURI("/some_path");
+        request.setContextPath("/bigWebApp");
+        request.setScheme("http");
+        request.setServerName("www.example.com");
+        request.setContextPath("/bigWebApp");
+        request.setServerPort(8888); // NB: Port we can't resolve
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        ep.afterPropertiesSet();
+        ep.commence(request, response);
+
+        // Response doesn't switch to HTTPS, as we didn't know HTTP port 8888 to
+        // HTTP port mapping
+        assertEquals("http://www.example.com:8888/bigWebApp/hello", response
+                .getRedirectedUrl());
+    }
+
+    public void testOperationWithOriginalRequestIncludes() throws Exception {
+        CaptchaEntryPoint ep = new CaptchaEntryPoint();
+        ep.setCaptchaFormUrl("/hello");
+        PortMapperImpl mapper = new PortMapperImpl();
+        mapper.getTranslatedPortMappings().put(new Integer(8888),
+                new Integer(1234));
+        ep.setPortMapper(mapper);
+
+        ep.setPortResolver(new MockPortResolver(8888, 1234));
+        ep.setIncludeOriginalRequest(true);
+        ep.afterPropertiesSet();
+
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        request.setMethod("post");
+        request.setRequestURI("/some_path");
+        request.setScheme("http");
+        request.setServerName("www.example.com");
+        // request.setContextPath("/bigWebApp");
+        // TODO correct this when the getRequestUrl from mock works...
+
+        request.setServerPort(8888); // NB: Port we can't resolve
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        ep.afterPropertiesSet();
+        ep.commence(request, response);
+        assertEquals(
+                "http://www.example.com:8888/hello?original_requestUrl="
+                        + URLEncoder.encode("http://www.example.com:8888/some_path", "UTF-8")
+                        + "&original_request_method=post",
+                response.getRedirectedUrl());
+
+        // test the query params
+        request.addParameter("name", "value");
+        response = new MockHttpServletResponse();
+        ep.commence(request, response);
+        assertEquals(
+                "http://www.example.com:8888/hello?original_requestUrl="
+                        + URLEncoder.encode("http://www.example.com:8888/some_path", "UTF-8")
+                        + "&original_request_method=post",
+                response.getRedirectedUrl());
+
+        // test the multiple query params
+        ep.setIncludeOriginalParameters(true);
+
+        request.addParameter("name", "value");
+        request.addParameter("name1", "value2");
+        response = new MockHttpServletResponse();
+        ep.commence(request, response);
+        assertEquals(
+                "http://www.example.com:8888/hello?original_requestUrl="
+                        + URLEncoder.encode("http://www.example.com:8888/some_path", "UTF-8")
+                        + "&original_request_method=post"
+                        + "&original_request_parameters="
+                        + URLEncoder.encode("name@@value;;name1@@value2", "UTF-8"),
+                response.getRedirectedUrl());
+
+        // test add parameter to captcha form url??
+
+        ep.setCaptchaFormUrl("/hello?toto=titi");
+        response = new MockHttpServletResponse();
+        ep.commence(request, response);
+        assertEquals(
+                "http://www.example.com:8888/hello?toto=titi&original_requestUrl="
+                        + URLEncoder.encode("http://www.example.com:8888/some_path", "UTF-8")
+                        + "&original_request_method=post" + "&original_request_parameters="
+                        + URLEncoder.encode("name@@value;;name1@@value2", "UTF-8"),
+                response.getRedirectedUrl());
+
+        // with forcing!!!
+        ep.setForceHttps(true);
+        response = new MockHttpServletResponse();
+        ep.commence(request, response);
+        assertEquals(
+                "https://www.example.com:1234/hello?toto=titi&original_requestUrl="
+                        + URLEncoder.encode("http://www.example.com:8888/some_path", "UTF-8") +
+                        "&original_request_method=post"
+                        + "&original_request_parameters="
+                        + URLEncoder.encode("name@@value;;name1@@value2", "UTF-8"),
+                response.getRedirectedUrl());
+
+    }
+
+    public void testOperationWithOutsideWebApp() throws Exception {
+        CaptchaEntryPoint ep = new CaptchaEntryPoint();
+        ep.setCaptchaFormUrl("https://www.jcaptcha.net/dotest/");
+        PortMapperImpl mapper = new PortMapperImpl();
+        mapper.getTranslatedPortMappings().put(new Integer(8888),
+                new Integer(1234));
+        ep.setPortMapper(mapper);
+
+        ep.setPortResolver(new MockPortResolver(8888, 1234));
+        ep.setIncludeOriginalRequest(true);
+        ep.setOutsideWebApp(true);
+
+        ep.afterPropertiesSet();
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        request.setRequestURI("/some_path");
+        request.setScheme("http");
+        request.setServerName("www.example.com");
+        request.setMethod("post");
+        // request.setContextPath("/bigWebApp");
+        // TODO correct this when the getRequestUrl from mock works...
+
+        request.setServerPort(8888); // NB: Port we can't resolve
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+
+        ep.afterPropertiesSet();
+        ep.commence(request, response);
+        assertEquals(
+                "https://www.jcaptcha.net/dotest/?original_requestUrl="
+                        + URLEncoder.encode("http://www.example.com:8888/some_path", "UTF-8")
+                        + "&original_request_method=post"
+                ,
+                response.getRedirectedUrl());
+
+        // test the query params
+        request.addParameter("name", "value");
+        response = new MockHttpServletResponse();
+        ep.commence(request, response);
+        assertEquals(
+                "https://www.jcaptcha.net/dotest/?original_requestUrl="
+                        + URLEncoder.encode("http://www.example.com:8888/some_path", "UTF-8") +
+                        "&original_request_method=post",
+                response.getRedirectedUrl());
+
+        // test the multiple query params
+        ep.setIncludeOriginalParameters(true);
+        request.addParameter("name", "value");
+        request.addParameter("name1", "value2");
+        response = new MockHttpServletResponse();
+        ep.commence(request, response);
+        assertEquals(
+                "https://www.jcaptcha.net/dotest/?original_requestUrl="
+                        + URLEncoder.encode("http://www.example.com:8888/some_path", "UTF-8")
+                        + "&original_request_method=post"
+                        + "&original_request_parameters="
+                        + URLEncoder.encode("name@@value;;name1@@value2", "UTF-8"),
+                response.getRedirectedUrl());
+
+        // test add parameter to captcha form url??
+
+        ep.setCaptchaFormUrl("https://www.jcaptcha.net/dotest/?toto=titi");
+        response = new MockHttpServletResponse();
+        ep.commence(request, response);
+        assertEquals(
+                "https://www.jcaptcha.net/dotest/?toto=titi&original_requestUrl="
+                        + URLEncoder.encode("http://www.example.com:8888/some_path", "UTF-8") +
+                        "&original_request_method=post"
+                        + "&original_request_parameters="
+                        + URLEncoder.encode("name@@value;;name1@@value2", "UTF-8"),
+                response.getRedirectedUrl());
+
+        // with forcing!!!
+        ep.setForceHttps(true);
+        response = new MockHttpServletResponse();
+        ep.commence(request, response);
+        assertEquals(
+                "https://www.jcaptcha.net/dotest/?toto=titi&original_requestUrl="
+                        + URLEncoder.encode("http://www.example.com:8888/some_path", "UTF-8") +
+                        "&original_request_method=post"
+                        + "&original_request_parameters="
+                        + URLEncoder.encode("name@@value;;name1@@value2", "UTF-8"),
+                response.getRedirectedUrl());
+
+    }
 
 }

+ 73 - 52
core/src/test/java/org/acegisecurity/captcha/CaptchaSecurityContextImplTests.java

@@ -1,64 +1,85 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * 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 net.sf.acegisecurity.captcha;
 
 import net.sf.acegisecurity.context.SecurityContextImplTests;
 
+/**
+ * Tests {@link CaptchaSecurityContextImpl}.
+ *
+ * @author marc antoine Garrigue
+ * @version $Id$
+ */
 public class CaptchaSecurityContextImplTests extends SecurityContextImplTests {
 
-	public void testDefaultValues() {
-		CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
-		assertEquals("should not be human", false, context.isHuman());
-		assertEquals("should be 0", 0, context
-				.getLastPassedCaptchaDateInMillis());
-		assertEquals("should be 0", 0, context
-				.getHumanRestrictedResourcesRequestsCount());
-	}
+    public void testDefaultValues() {
+        CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
+        assertEquals("should not be human", false, context.isHuman());
+        assertEquals("should be 0", 0, context
+                .getLastPassedCaptchaDateInMillis());
+        assertEquals("should be 0", 0, context
+                .getHumanRestrictedResourcesRequestsCount());
+    }
 
-	public void testSetHuman() {
-		CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
-		long now = System.currentTimeMillis();
-		context.setHuman();
-		assertEquals("should be human", true, context.isHuman());
-		assertTrue("should be more than 0", context
-				.getLastPassedCaptchaDateInMillis()
-				- now >= 0);
-		assertTrue("should be less than 0,1 seconde", context
-				.getLastPassedCaptchaDateInMillis()
-				- now < 100);
-		assertEquals("should be 0", 0, context
-				.getHumanRestrictedResourcesRequestsCount());
-	}
+    public void testSetHuman() {
+        CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
+        long now = System.currentTimeMillis();
+        context.setHuman();
+        assertEquals("should be human", true, context.isHuman());
+        assertTrue("should be more than 0", context
+                .getLastPassedCaptchaDateInMillis()
+                - now >= 0);
+        assertTrue("should be less than 0,1 seconde", context
+                .getLastPassedCaptchaDateInMillis()
+                - now < 100);
+        assertEquals("should be 0", 0, context
+                .getHumanRestrictedResourcesRequestsCount());
+    }
 
-	public void testIncrementRequests() {
-		CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
-		context.setHuman();
-		assertEquals("should be human", true, context.isHuman());
-		assertEquals("should be 0", 0, context
-				.getHumanRestrictedResourcesRequestsCount());
-		context.incrementHumanRestrictedRessoucesRequestsCount();
-		assertEquals("should be 1", 1, context
-				.getHumanRestrictedResourcesRequestsCount());
-	}
+    public void testIncrementRequests() {
+        CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
+        context.setHuman();
+        assertEquals("should be human", true, context.isHuman());
+        assertEquals("should be 0", 0, context
+                .getHumanRestrictedResourcesRequestsCount());
+        context.incrementHumanRestrictedRessoucesRequestsCount();
+        assertEquals("should be 1", 1, context
+                .getHumanRestrictedResourcesRequestsCount());
+    }
 
-	public void testResetHuman() {
-		CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
-		context.setHuman();
-		assertEquals("should be human", true, context.isHuman());
-		assertEquals("should be 0", 0, context
-				.getHumanRestrictedResourcesRequestsCount());
-		context.incrementHumanRestrictedRessoucesRequestsCount();
-		assertEquals("should be 1", 1, context
-				.getHumanRestrictedResourcesRequestsCount());
-		long now = System.currentTimeMillis();
-		context.setHuman();
-		assertEquals("should be 0", 0, context
-				.getHumanRestrictedResourcesRequestsCount());
-		assertTrue("should be more than 0", context
-				.getLastPassedCaptchaDateInMillis()
-				- now >= 0);
-		assertTrue("should be less than 0,1 seconde", context
-				.getLastPassedCaptchaDateInMillis()
-				- now < 100);
+    public void testResetHuman() {
+        CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
+        context.setHuman();
+        assertEquals("should be human", true, context.isHuman());
+        assertEquals("should be 0", 0, context
+                .getHumanRestrictedResourcesRequestsCount());
+        context.incrementHumanRestrictedRessoucesRequestsCount();
+        assertEquals("should be 1", 1, context
+                .getHumanRestrictedResourcesRequestsCount());
+        long now = System.currentTimeMillis();
+        context.setHuman();
+        assertEquals("should be 0", 0, context
+                .getHumanRestrictedResourcesRequestsCount());
+        assertTrue("should be more than 0", context
+                .getLastPassedCaptchaDateInMillis()
+                - now >= 0);
+        assertTrue("should be less than 0,1 seconde", context
+                .getLastPassedCaptchaDateInMillis()
+                - now < 100);
 
-	}
+    }
 
 }

+ 104 - 72
core/src/test/java/org/acegisecurity/captcha/CaptchaValidationProcessingFilterTests.java

@@ -1,83 +1,115 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * 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 net.sf.acegisecurity.captcha;
 
 import junit.framework.TestCase;
 import net.sf.acegisecurity.context.SecurityContextHolder;
 import net.sf.acegisecurity.util.MockFilterChain;
-
 import org.springframework.mock.web.MockHttpServletRequest;
 
+/**
+ * Tests {@link CaptchaValidationProcessingFilter}.
+ *
+ * @author marc antoine Garrigue
+ * @version $Id$
+ */
 public class CaptchaValidationProcessingFilterTests extends TestCase {
 
-	/*
-	 */
-	public void testAfterPropertiesSet() throws Exception {
-		CaptchaValidationProcessingFilter filter = new CaptchaValidationProcessingFilter();
-
-		try {
-			filter.afterPropertiesSet();
-			fail("should have thrown an invalid argument exception");
-		} catch (Exception e) {
-			assertTrue("should be an InvalidArgumentException",
-					IllegalArgumentException.class.isAssignableFrom(e
-							.getClass()));
-		}
-		filter.setCaptchaService(new MockCaptchaServiceProxy());
-		filter.afterPropertiesSet();
-
-	}
-
-	/*
-	 * Test method for
-	 * 'net.sf.acegisecurity.captcha.CaptchaValidationProcessingFilter.doFilter(ServletRequest,
-	 * ServletResponse, FilterChain)'
-	 */
-	public void testDoFilterWithoutRequestParameter() throws Exception {
-		CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
-		SecurityContextHolder.setContext(context);
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		CaptchaValidationProcessingFilter filter = new CaptchaValidationProcessingFilter();
-		MockCaptchaServiceProxy service = new MockCaptchaServiceProxy();
-		MockFilterChain chain = new MockFilterChain(true);
-		filter.setCaptchaService(service);
-		filter.doFilter(request, null, chain);
-		assertFalse("proxy should not have been called", service.hasBeenCalled);
-		assertFalse("context should not have been updated", context.isHuman());
-		// test with valid
-		service.valid = true;
-		filter.doFilter(request, null, chain);
-		assertFalse("proxy should not have been called", service.hasBeenCalled);
-		assertFalse("context should not have been updated", context.isHuman());
-
-	}
-
-	/*
-	 * Test method for
-	 * 'net.sf.acegisecurity.captcha.CaptchaValidationProcessingFilter.doFilter(ServletRequest,
-	 * ServletResponse, FilterChain)'
-	 */
-	public void testDoFilterWithRequestParameter() throws Exception {
-		CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
-		SecurityContextHolder.setContext(context);
-
-		MockHttpServletRequest request = new MockHttpServletRequest();
-		request
-				.addParameter(
-						CaptchaValidationProcessingFilter.CAPTCHA_VALIDATION_SECURITY_PARAMETER_KEY,
-						"");
-
-		CaptchaValidationProcessingFilter filter = new CaptchaValidationProcessingFilter();
-		MockCaptchaServiceProxy service = new MockCaptchaServiceProxy();
-		MockFilterChain chain = new MockFilterChain(true);
-		filter.setCaptchaService(service);
-		filter.doFilter(request, null, chain);
-		assertTrue("should have been called", service.hasBeenCalled);
-		assertFalse("context should not have been updated", context.isHuman());
-		// test with valid
-		service.valid = true;
-		filter.doFilter(request, null, chain);
-		assertTrue("should have been called", service.hasBeenCalled);
-		assertTrue("context should have been updated", context.isHuman());
-
-	}
+    /*
+      */
+    public void testAfterPropertiesSet() throws Exception {
+        CaptchaValidationProcessingFilter filter = new CaptchaValidationProcessingFilter();
+
+        try {
+            filter.afterPropertiesSet();
+            fail("should have thrown an invalid argument exception");
+        } catch (Exception e) {
+            assertTrue("should be an InvalidArgumentException",
+                    IllegalArgumentException.class.isAssignableFrom(e
+                            .getClass()));
+        }
+
+        filter.setCaptchaService(new MockCaptchaServiceProxy());
+        filter.afterPropertiesSet();
+        filter.setCaptchaValidationParameter(null);
+        try {
+            filter.afterPropertiesSet();
+            fail("should have thrown an invalid argument exception");
+        } catch (Exception e) {
+            assertTrue("should be an InvalidArgumentException",
+                    IllegalArgumentException.class.isAssignableFrom(e
+                            .getClass()));
+        }
+
+
+    }
+
+    /*
+      * Test method for
+      * 'net.sf.acegisecurity.captcha.CaptchaValidationProcessingFilter.doFilter(ServletRequest,
+      * ServletResponse, FilterChain)'
+      */
+    public void testDoFilterWithoutRequestParameter() throws Exception {
+        CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
+        SecurityContextHolder.setContext(context);
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        CaptchaValidationProcessingFilter filter = new CaptchaValidationProcessingFilter();
+        MockCaptchaServiceProxy service = new MockCaptchaServiceProxy();
+        MockFilterChain chain = new MockFilterChain(true);
+        filter.setCaptchaService(service);
+        filter.doFilter(request, null, chain);
+        assertFalse("proxy should not have been called", service.hasBeenCalled);
+        assertFalse("context should not have been updated", context.isHuman());
+        // test with valid
+        service.valid = true;
+        filter.doFilter(request, null, chain);
+        assertFalse("proxy should not have been called", service.hasBeenCalled);
+        assertFalse("context should not have been updated", context.isHuman());
+
+    }
+
+    /*
+      * Test method for
+      * 'net.sf.acegisecurity.captcha.CaptchaValidationProcessingFilter.doFilter(ServletRequest,
+      * ServletResponse, FilterChain)'
+      */
+    public void testDoFilterWithRequestParameter() throws Exception {
+        CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
+        SecurityContextHolder.setContext(context);
+
+        MockHttpServletRequest request = new MockHttpServletRequest();
+
+        CaptchaValidationProcessingFilter filter = new CaptchaValidationProcessingFilter();
+        request
+                .addParameter(
+                        filter.getCaptchaValidationParameter(),
+                        "");
+
+        MockCaptchaServiceProxy service = new MockCaptchaServiceProxy();
+        MockFilterChain chain = new MockFilterChain(true);
+        filter.setCaptchaService(service);
+        filter.doFilter(request, null, chain);
+        assertTrue("should have been called", service.hasBeenCalled);
+        assertFalse("context should not have been updated", context.isHuman());
+        // test with valid
+        service.valid = true;
+        filter.doFilter(request, null, chain);
+        assertTrue("should have been called", service.hasBeenCalled);
+        assertTrue("context should have been updated", context.isHuman());
+
+    }
 
 }

+ 10 - 8
core/src/test/java/org/acegisecurity/captcha/MockCaptchaServiceProxy.java

@@ -15,18 +15,20 @@
 
 package net.sf.acegisecurity.captcha;
 
-import javax.servlet.ServletRequest;
-
+/**
+ * @author marc antoine Garrigue
+ * @version $Id$
+ */
 public class MockCaptchaServiceProxy implements CaptchaServiceProxy {
 
-	public boolean valid = false;
+    public boolean valid = false;
 
-	public boolean hasBeenCalled = false;
+    public boolean hasBeenCalled = false;
 
-	public boolean validateRequest(ServletRequest request) {
-		hasBeenCalled = true;
-		return valid;
+    public boolean validateReponseForId(String id, Object response) {
+        hasBeenCalled = true;
+        return valid;
 
-	}
+    }
 
 }