Initial commit for captcha adapter

This commit is contained in:
Marc-Antoine Garrigue 2005-07-19 12:35:50 +00:00
parent a67b529734
commit 3287439421
11 changed files with 2031 additions and 0 deletions

View File

@ -0,0 +1,361 @@
/* 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 java.io.IOException;
import java.util.Iterator;
import javax.servlet.ServletException;
import net.sf.acegisecurity.ConfigAttribute;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import net.sf.acegisecurity.context.SecurityContextHolder;
import net.sf.acegisecurity.intercept.web.FilterInvocation;
import net.sf.acegisecurity.securechannel.ChannelEntryPoint;
import net.sf.acegisecurity.securechannel.ChannelProcessor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* <p>
* Ensures the user has enougth human privileges by review of the
* {@link net.sf.acegisecurity.captcha.CaptchaSecurityContext}.
* </p>
*
* <P>
* The class takes 3 required attributes :
* <ul>
* <li>maxRequestsBeforeFirstTest : used by
* {@link #getRequiresHumanAfterMaxRequestsKeyword()} and
* {@link #getRequiresHumanAfterMaxMillisKeyword()}<br>
* default value = 0 (ie first request).</li>
* <li>maxRequestsBeforeReTest : used by
* {@link #getRequiresHumanAfterMaxMillisKeyword()}<br>
* default value = -1 (ie disabled, once in a {@link CaptchaSecurityContext}'s
* life).</li>
* <li>maxMillisBeforeReTest: used by
* {@link #getRequiresHumanAfterMaxMillisKeyword()} <br>
* default value = -1 (ie disabled, once in a {@link CaptchaSecurityContext}'s
* life).</li>
* </ul>
* The class responds to two case-sensitive keywords :
* <ul>
* <li>{@link #getRequiresHumanAfterMaxRequestsKeyword()} <br>
* default value = <code>REQUIRES_HUMAN_AFTER_MAX_REQUESTS</code> <br>
* if detected, checks if :
* <ul>
* <ul>
* <li><code>{@link CaptchaSecurityContext#isHuman()} == true</code> </li>
* <li><b>or</b></li>
* <li><code>{@link CaptchaSecurityContext#getHumanRestrictedResourcesRequestsCount()} < maxRequestsBeforeFirstTest</code></b></li>
* </ul>
* <li><b>and</b></li>
* <ul>
* <li><code>{@link CaptchaSecurityContext#getHumanRestrictedResourcesRequestsCount()} < maxRequestsBeforeReTest </code></li>
* <li><b>or</b></li>
* <li><code>maxRequestsBeforeReTest < 0 </code></b></li>
* </ul>
* </ul>
* </li>
*
*
*
*
*
*
*
* <li>{@link #getRequiresHumanUntilMaxRequestsKeyword()} <br>
* default value = <code>REQUIRES_HUMAN_AFTER_MAX_MILLIS</code> <br>
* if detected, checks if :
*
* <ul>
* <ul>
* <li><code>{@link CaptchaSecurityContext#isHuman()} == true</code> </li>
* <li><b>or</b></li>
* <li><code>{@link CaptchaSecurityContext#getHumanRestrictedResourcesRequestsCount()} < =maxRequestsBeforeFirstTest</code></b></li>
* </ul>
* <li><b>and</b></li>
* <ul>
* <li><code>System.currentTimeMillis()-{@link CaptchaSecurityContext#getLastPassedCaptchaDateInMillis()} <= maxMillisBeforeReTest </code></li>
* <li><b>or</b></li>
* <li><code>maxMillisBeforeReTest < 0 </code></b></li>
* </ul>
* </ul>
* </li>
* </ul>
* </p>
*
* <p>
* <u>Examples : to ensure an url is accessed only by human that pass a captcha
* (assuming you are using the
* {@link net.sf.acegisecurity.context.HttpSessionContextIntegrationFilter})
* </u><br>
* <ul>
* <li>Once in a session and at first request : use the <br>
* REQUIRES_HUMAN_AFTER_MAX_REQUESTS keyword <br>
* with a maxRequestsBeforeFirstTest=0<br>
* and a maxRequestsBeforeReTest=-1<br>
* </li>
* <br>
* &nbsp;
* <li>Once in a session and only after 3 requests : use the <br>
* REQUIRES_HUMAN_AFTER_MAX_REQUESTS keyword <br>
* with a maxRequestsBeforeFirstTest=3</li>
* and a maxRequestsBeforeReTest=-1<br>
* <br>
* &nbsp;
* <li>Every request and only after 5 requests : use the <br>
* REQUIRES_HUMAN_AFTER_MAX_REQUESTS <br>
* with a maxRequestsBeforeReTest=0<br>
* and a maxRequestsBeforeFirstTest=5</li>
* <br>
* &nbsp;
* <li>Every 3 requests and every minute : use the <br>
* REQUIRES_HUMAN_AFTER_MAX_MILLIS keywords <br>
* with a maxMillisBeforeReTest=6000 <br>
* and a maxRequestsBeforeFirstTest=3</li>
* <br>
* &nbsp;
* <li>Every 20 requests and every hour and only after 100 requests : use the
* <br>
* REQUIRES_HUMAN_AFTER_MAX_REQUESTS <br>
* and the REQUIRES_HUMAN_AFTER_MAX_MILLIS <br>
* and the REQUIRES_HUMAN_AFTER_MAX_REQUESTS keywords <br>
* with a maxRequestsBeforeReTest=20 <br>
* and a maxMillisBeforeReTest=3600000 <br>
* and amaxRequestsBeforeFirstTest=1000</li>
*
* </ul>
*
*
* @author marc antoine Garrigue
* @version $Id$
*/
public class CaptchaChannelProcessor implements ChannelProcessor,
InitializingBean {
// ~ Static fields/initializers
// =============================================
private static final Log logger = LogFactory
.getLog(CaptchaChannelProcessor.class);
private String requiresHumanAfterMaxRequestsKeyword = "REQUIRES_HUMAN_AFTER_MAX_REQUESTS";
private String requiresHumanAfterMaxMillisKeyword = "REQUIRES_HUMAN_AFTER_MAX_MILLIS";
private ChannelEntryPoint entryPoint;
private int maxRequestsBeforeReTest = -1;
private int maxRequestsBeforeFirstTest = 0;
private long maxMillisBeforeReTest = -1;
public String getRequiresHumanAfterMaxMillisKeyword() {
return requiresHumanAfterMaxMillisKeyword;
}
public void setRequiresHumanAfterMaxMillisKeyword(
String requiresHumanAfterMaxMillis) {
this.requiresHumanAfterMaxMillisKeyword = requiresHumanAfterMaxMillis;
}
public void setRequiresHumanAfterMaxRequestsKeyword(
String requiresHumanAfterMaxRequestsKeyword) {
this.requiresHumanAfterMaxRequestsKeyword = requiresHumanAfterMaxRequestsKeyword;
}
public ChannelEntryPoint getEntryPoint() {
return entryPoint;
}
public void setEntryPoint(ChannelEntryPoint entryPoint) {
this.entryPoint = entryPoint;
}
public int getMaxRequestsBeforeReTest() {
return maxRequestsBeforeReTest;
}
public void setMaxRequestsBeforeReTest(int maxRequestsBeforeReTest) {
this.maxRequestsBeforeReTest = maxRequestsBeforeReTest;
}
public String getRequiresHumanAfterMaxRequestsKeyword() {
return requiresHumanAfterMaxRequestsKeyword;
}
public int getMaxRequestsBeforeFirstTest() {
return maxRequestsBeforeFirstTest;
}
public void setMaxRequestsBeforeFirstTest(int maxRequestsBeforeFirstTest) {
this.maxRequestsBeforeFirstTest = maxRequestsBeforeFirstTest;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(entryPoint, "entryPoint required");
}
public long getMaxMillisBeforeReTest() {
return maxMillisBeforeReTest;
}
public void setMaxMillisBeforeReTest(long maxMillisBeforeReTest) {
this.maxMillisBeforeReTest = maxMillisBeforeReTest;
}
public void decide(FilterInvocation invocation,
ConfigAttributeDefinition config) throws IOException,
ServletException {
if ((invocation == null) || (config == null)) {
throw new IllegalArgumentException("Nulls cannot be provided");
}
CaptchaSecurityContext context = (CaptchaSecurityContext) SecurityContextHolder
.getContext();
Iterator iter = config.getConfigAttributes();
boolean shouldRedirect = true;
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
shouldRedirect = true;
// break at first unsatisfy contraint
break;
}
}
}
if (shouldRedirect) {
logger
.debug("context is not allowed to access ressource, redirect to captcha entry point");
redirectToEntryPoint(invocation);
} else {
// if we reach this point, we forward the request so
// increment it
logger
.debug("context is allowed to access ressource, increment rectricted ressource requests count ");
context.incrementHumanRestrictedRessoucesRequestsCount();
}
}
private boolean isContextValidForAttribute(CaptchaSecurityContext context,
ConfigAttribute attribute) {
boolean valid = false;
if ((attribute != null) || (attribute.getAttribute() != null)) {
// test the REQUIRES_HUMAN_AFTER_MAX_REQUESTS keyword
if (attribute.getAttribute().equals(
getRequiresHumanAfterMaxRequestsKeyword())) {
if (isContextValidConcerningHumanOrFirstTest(context)
&& isContextValidConcerningReTest(context)) {
valid = true;
}
}
// test the REQUIRES_HUMAN_AFTER_MAX_MILLIS keyword
if (attribute.getAttribute().equals(
getRequiresHumanAfterMaxMillisKeyword())) {
if (isContextValidConcerningHumanOrFirstTest(context)
&& isContextValidConcerningMaxMillis(context)) {
valid = true;
}
}
}
return valid;
}
private boolean isContextValidConcerningHumanOrFirstTest(
CaptchaSecurityContext context) {
if (context.isHuman()
|| context.getHumanRestrictedResourcesRequestsCount() < maxRequestsBeforeFirstTest) {
logger
.debug("context is valid concerning humanity or request count < maxRequestsBeforeFirstTest");
return true;
} else {
logger
.debug("context is not valid concerning humanity and request count > maxRequestsBeforeFirstTest");
return false;
}
}
private boolean isContextValidConcerningReTest(
CaptchaSecurityContext context) {
if (context.getHumanRestrictedResourcesRequestsCount() < maxRequestsBeforeReTest
|| maxRequestsBeforeReTest < 0) {
logger.debug("context is valid concerning reTest");
return true;
} else {
logger.debug("context is not valid concerning reTest");
return false;
}
}
private boolean isContextValidConcerningMaxMillis(
CaptchaSecurityContext context) {
if (System.currentTimeMillis()
- context.getLastPassedCaptchaDateInMillis() < maxMillisBeforeReTest
|| maxMillisBeforeReTest < 0) {
logger.debug("context is valid concerning maxMillis");
return true;
} else {
logger.debug("context is not valid concerning maxMillis");
return false;
}
}
private void redirectToEntryPoint(FilterInvocation invocation)
throws IOException, ServletException {
logger
.debug("security constraints not repected : redirecting to entry point");
entryPoint.commence(invocation.getRequest(), invocation.getResponse());
return;
}
public boolean supports(ConfigAttribute attribute) {
if ((attribute != null)
&& (attribute.getAttribute() != null)
&& (attribute.getAttribute().equals(
getRequiresHumanAfterMaxRequestsKeyword()) || attribute
.getAttribute().equals(
getRequiresHumanAfterMaxMillisKeyword())
)) {
return true;
} else {
return false;
}
}
}

View File

@ -0,0 +1,287 @@
/* 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 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;
/**
* The captcha entry point : redirect to the captcha test page. <br/>
*
* 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/>
*
* @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);
}
}
}

View File

@ -0,0 +1,55 @@
/* Copyright 2004, 2005 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.SecurityContext;
/**
* Interface that add humanity concerns to the SecurityContext
*
* @author marc antoine garrigue
*/
public interface CaptchaSecurityContext extends SecurityContext {
/**
* @return true if the current user has already passed a captcha.
*/
boolean isHuman();
/**
* set human attribute, should called after captcha validation.
*
* @param human
*/
void setHuman();
/**
*
* @return number of human restricted resources requests since the last
* passed captcha.
*/
int getHumanRestrictedResourcesRequestsCount();
/**
* @return the date of the last passed Captcha in millis, 0 if the user
* never passed captcha.
*/
long getLastPassedCaptchaDateInMillis();
/**
* Method to increment the human Restricted Resrouces Requests Count;
*/
void incrementHumanRestrictedRessoucesRequestsCount();
}

View File

@ -0,0 +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.SecurityContextImpl;
/**
* @author mag
*
*/
public class CaptchaSecurityContextImpl extends SecurityContextImpl implements
CaptchaSecurityContext {
private boolean human;
private long lastPassedCaptchaDate;
private int humanRestrictedResourcesRequestsCount;
/**
*
*/
public CaptchaSecurityContextImpl() {
super();
human = false;
lastPassedCaptchaDate = 0;
humanRestrictedResourcesRequestsCount = 0;
}
/*
* (non-Javadoc)
*
* @see net.sf.acegisecurity.context.CaptchaSecurityContext#isHuman()
*/
public boolean isHuman() {
return human;
}
/**
* reset the lastPassedCaptchaDate and count.
*/
public void setHuman() {
this.human = true;
this.lastPassedCaptchaDate = System.currentTimeMillis();
this.humanRestrictedResourcesRequestsCount = 0;
}
/*
* (non-Javadoc)
*
* @see net.sf.acegisecurity.context.CaptchaSecurityContext#getHumanRestrictedResourcesRequestsCount()
*/
public int getHumanRestrictedResourcesRequestsCount() {
return humanRestrictedResourcesRequestsCount;
}
/*
* (non-Javadoc)
*
* @see net.sf.acegisecurity.context.CaptchaSecurityContext#getLastPassedCaptchaDateInMillis()
*/
public long getLastPassedCaptchaDateInMillis() {
return lastPassedCaptchaDate;
}
/**
* Method to increment the human Restricted Resrouces Requests Count;
*/
public void incrementHumanRestrictedRessoucesRequestsCount() {
humanRestrictedResourcesRequestsCount++;
};
}

View File

@ -0,0 +1,17 @@
package net.sf.acegisecurity.captcha;
import javax.servlet.ServletRequest;
/**
* Provide a common interface for captcha validation.
*
* @author marc antoine Garrigue
* @version $Id$
*/
public interface CaptchaServiceProxy {
/**
* @return true if the request is validated by the back end captcha service.
*/
boolean validateRequest(ServletRequest request);
}

View File

@ -0,0 +1,126 @@
/* 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 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;
/**
* 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 {
}
}

View File

@ -0,0 +1,536 @@
/* 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 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}
* @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"));
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(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");
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");
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");
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);
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());
}
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"));
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);
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());
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(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("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());
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(0, processor.getMaxRequestsBeforeFirstTest());
processor.setMaxRequestsBeforeFirstTest(1);
assertEquals(1, processor.getMaxRequestsBeforeFirstTest());
assertEquals(-1, processor.getMaxRequestsBeforeReTest());
processor.setMaxRequestsBeforeReTest(11);
assertEquals(11, processor.getMaxRequestsBeforeReTest());
assertEquals(-1, processor.getMaxMillisBeforeReTest());
processor.setMaxMillisBeforeReTest(111);
assertEquals(111, processor.getMaxMillisBeforeReTest());
assertTrue(processor.getEntryPoint() == null);
processor.setEntryPoint(new CaptchaEntryPoint());
assertTrue(processor.getEntryPoint() != 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());
}
}
public void testMissingKeyword() throws Exception {
CaptchaChannelProcessor processor = new CaptchaChannelProcessor();
processor.setRequiresHumanAfterMaxMillisKeyword(null);
try {
processor.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
processor.setRequiresHumanAfterMaxMillisKeyword("");
try {
processor.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
processor.setRequiresHumanAfterMaxRequestsKeyword("");
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")));
}
}

View File

@ -0,0 +1,385 @@
/* 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 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;
/**
* Tests {@link RetryWithHttpEntryPoint}.
*
* @author Ben Alex
* @version $Id: RetryWithHttpEntryPointTests.java,v 1.4 2005/04/11 01:07:02
* luke_t Exp $
*/
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());
}
}

View File

@ -0,0 +1,64 @@
package net.sf.acegisecurity.captcha;
import net.sf.acegisecurity.context.SecurityContextImplTests;
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 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 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);
}
}

View File

@ -0,0 +1,83 @@
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;
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());
}
}

View File

@ -0,0 +1,32 @@
/* 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;
public class MockCaptchaServiceProxy implements CaptchaServiceProxy {
public boolean valid = false;
public boolean hasBeenCalled = false;
public boolean validateRequest(ServletRequest request) {
hasBeenCalled = true;
return valid;
}
}