1
0
mirror of synced 2026-08-05 17:57:15 +00:00

SEC-674: Created new project modules for cas, captcha, acls and taglibs

This commit is contained in:
Luke Taylor
2008-02-19 20:30:53 +00:00
parent 59651f5214
commit 2dd9faabc0
149 changed files with 425 additions and 218 deletions
+34
View File
@@ -0,0 +1,34 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>spring-security-parent</artifactId>
<groupId>org.springframework.security</groupId>
<version>2.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-captcha</artifactId>
<name>Spring Security - Catptcha Module</name>
<version>2.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-mock</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,63 @@
/* Copyright 2004, 2005, 2006 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.
*/
/*
* Copyright (c) 2005 Your Corporation. All Rights Reserved.
*/
package org.springframework.security.captcha;
/**
* <p>return false if ny CaptchaChannelProcessorTemplate of mapped urls has been requested more than thresold; <br>
* Default keyword : REQUIRES_CAPTCHA_ABOVE_THRESOLD_REQUESTS</p>
*
* @author Marc-Antoine Garrigue
* @version $Id$
*/
public class AlwaysTestAfterMaxRequestsCaptchaChannelProcessor extends CaptchaChannelProcessorTemplate {
//~ Static fields/initializers =====================================================================================
/** Keyword for this channelProcessor */
public static final String DEFAULT_KEYWORD = "REQUIRES_CAPTCHA_ABOVE_THRESHOLD_REQUESTS";
//~ Constructors ===================================================================================================
/**
* Constructor
*/
public AlwaysTestAfterMaxRequestsCaptchaChannelProcessor() {
this.setKeyword(DEFAULT_KEYWORD);
}
//~ Methods ========================================================================================================
/**
* Verify whether the context is valid concerning humanity
*
* @param context
*
* @return true if valid, false otherwise
*/
boolean isContextValidConcerningHumanity(CaptchaSecurityContext context) {
if (context.getHumanRestrictedResourcesRequestsCount() < getThreshold()) {
logger.debug("context is valid : request count < thresold");
return true;
} else {
logger.debug("context is not valid : request count > thresold");
return false;
}
}
}
@@ -0,0 +1,61 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.captcha;
/**
* <p>return false if thresold is greater than millis since last captcha test has occured;<br>
* Default keyword : REQUIRES_CAPTCHA_AFTER_THRESOLD_IN_MILLIS</p>
*
* @author Marc-Antoine Garrigue
* @version $Id$
*/
public class AlwaysTestAfterTimeInMillisCaptchaChannelProcessor extends CaptchaChannelProcessorTemplate {
//~ Static fields/initializers =====================================================================================
/** Keyword for this channelProcessor */
public static final String DEFAULT_KEYWORD = "REQUIRES_CAPTCHA_AFTER_THRESHOLD_IN_MILLIS";
//~ Constructors ===================================================================================================
/**
* Constructor
*/
public AlwaysTestAfterTimeInMillisCaptchaChannelProcessor() {
this.setKeyword(DEFAULT_KEYWORD);
}
//~ Methods ========================================================================================================
/**
* Verify wheter the context is valid concerning humanity
*
* @param context the CaptchaSecurityContext
*
* @return true if valid, false otherwise
*/
boolean isContextValidConcerningHumanity(CaptchaSecurityContext context) {
if ((System.currentTimeMillis() - context.getLastPassedCaptchaDateInMillis()) < getThreshold()) {
logger.debug("context is valid : last passed captcha date - current time < thresold");
return true;
} else {
logger.debug("context is not valid : last passed captcha date - current time > thresold");
return false;
}
}
}
@@ -0,0 +1,86 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.captcha;
import org.springframework.util.Assert;
/**
* <p>return false if thresold is lower than average time millis between any CaptchaChannelProcessorTemplate mapped
* urls requests and is human;<br>
* Default keyword : REQUIRES_CAPTCHA_BELOW_AVERAGE_TIME_IN_MILLIS_REQUESTS <br>
* Note : before first humanity check</p>
*
* @author Marc-Antoine Garrigue
* @version $Id$
*/
public class AlwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor extends CaptchaChannelProcessorTemplate {
//~ Static fields/initializers =====================================================================================
/** Keyword for this channelProcessor */
public static final String DEFAULT_KEYWORD = "REQUIRES_CAPTCHA_BELOW_AVERAGE_TIME_IN_MILLIS_REQUESTS";
//~ Constructors ===================================================================================================
/**
* Constructor
*/
public AlwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor() {
this.setKeyword(DEFAULT_KEYWORD);
}
//~ Methods ========================================================================================================
/**
* Verify if thresold is &gt; 0
*
* @throws Exception if false
*/
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
Assert.isTrue(getThreshold() > 0, "thresold must be > 0");
}
/**
* Verify wheter the context is valid concerning humanity
*
* @param context
*
* @return true if valid, false otherwise
*/
boolean isContextValidConcerningHumanity(CaptchaSecurityContext context) {
int req = context.getHumanRestrictedResourcesRequestsCount();
float thresold = getThreshold();
float duration = System.currentTimeMillis() - context.getLastPassedCaptchaDateInMillis();
float average;
if (req == 0) {
average = thresold + 1;
} else {
average = duration / req;
}
if (context.isHuman() && (average > thresold)) {
logger.debug("context is valid : average time between requests < thresold && is human");
return true;
} else {
logger.debug("context is not valid : request count > thresold or is not human");
return false;
}
}
}
@@ -0,0 +1,152 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.captcha;
import org.springframework.security.ConfigAttribute;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.intercept.web.FilterInvocation;
import org.springframework.security.securechannel.ChannelEntryPoint;
import org.springframework.security.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;
import java.io.IOException;
import java.util.Iterator;
import javax.servlet.ServletException;
/**
* <p>CaptchaChannel template : Ensures the user has enough human privileges by review of the {@link
* CaptchaSecurityContext} and using an abstract routine {@link
* #isContextValidConcerningHumanity(CaptchaSecurityContext)} (implemented by sub classes)</p>
* <P>The component uses 2 main parameters for its configuration :
* <ul>
* <li>a keyword to be mapped to urls in the {@link
* org.springframework.security.securechannel.ChannelProcessingFilter} configuration<br>
* default value provided by sub classes.</li>
* <li>and a thresold : used by the routine {@link
* #isContextValidConcerningHumanity(CaptchaSecurityContext)} to evaluate whether the {@link
* CaptchaSecurityContext} is valid default value = 0</li>
* </ul>
* </p>
*
* @author marc antoine Garrigue
* @version $Id$
*/
public abstract class CaptchaChannelProcessorTemplate implements ChannelProcessor, InitializingBean {
//~ Instance fields ================================================================================================
private ChannelEntryPoint entryPoint;
protected Log logger = LogFactory.getLog(this.getClass());
private String keyword = null;
private int thresold = 0;
//~ Methods ========================================================================================================
/**
* Verify if entryPoint and keyword are ok
*
* @throws Exception if not
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(entryPoint, "entryPoint required");
Assert.hasLength(keyword, "keyword required");
}
public void decide(FilterInvocation invocation, ConfigAttributeDefinition config)
throws IOException, ServletException {
if ((invocation == null) || (config == null)) {
throw new IllegalArgumentException("Nulls cannot be provided");
}
CaptchaSecurityContext context = null;
context = (CaptchaSecurityContext) SecurityContextHolder.getContext();
Iterator iter = config.getConfigAttributes().iterator();
while (iter.hasNext()) {
ConfigAttribute attribute = (ConfigAttribute) iter.next();
if (supports(attribute)) {
logger.debug("supports this attribute : " + attribute);
if (!isContextValidConcerningHumanity(context)) {
logger.debug("context is not allowed to access ressource, redirect to captcha entry point");
redirectToEntryPoint(invocation);
} else {
logger.debug("has been successfully checked this keyword, increment request count");
context.incrementHumanRestrictedResourcesRequestsCount();
}
} else {
logger.debug("do not support this attribute");
}
}
}
public ChannelEntryPoint getEntryPoint() {
return entryPoint;
}
public String getKeyword() {
return keyword;
}
public int getThreshold() {
return thresold;
}
abstract boolean isContextValidConcerningHumanity(CaptchaSecurityContext context);
private void redirectToEntryPoint(FilterInvocation invocation)
throws IOException, ServletException {
if (logger.isDebugEnabled()) {
logger.debug("context is not valid : redirecting to entry point");
}
entryPoint.commence(invocation.getRequest(), invocation.getResponse());
}
public void setEntryPoint(ChannelEntryPoint entryPoint) {
this.entryPoint = entryPoint;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public void setThreshold(int thresold) {
this.thresold = thresold;
}
public boolean supports(ConfigAttribute attribute) {
if ((attribute != null) && (keyword.equals(attribute.getAttribute()))) {
return true;
} else {
return false;
}
}
}
@@ -0,0 +1,397 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.captcha;
import org.springframework.security.securechannel.ChannelEntryPoint;
import org.springframework.security.util.PortMapper;
import org.springframework.security.util.PortMapperImpl;
import org.springframework.security.util.PortResolver;
import org.springframework.security.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 java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
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;
/**
* The captcha entry point : redirect to the captcha test page.
* <p>
* This entry point can force the use of SSL : see {@link #getForceHttps()}
* </p>
* <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 />
* The 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 original parameters string is contructed using :
* <ul>
* <li>a parameter separator {@link #getOriginalRequestParametersSeparator()}</li>
* <li>a parameter name value pair separator for each parameter {@link
* #getOriginalRequestParametersNameValueSeparator()}</li>
* </ul>
* </li>
* </ul>
* <br><br>
* Default values :
* <pre>
* forceHttps = false
* includesOriginalRequest = true
* includesOriginalParameters = false
* isOutsideWebApp = false
* originalRequestUrlParameterName = original_requestUrl
* originalRequestParametersParameterName = original_request_parameters
* originalRequestParametersNameValueSeparator = __
* originalRequestParametersSeparator = ;;
* originalRequestMethodParameterName = original_request_method
* urlEncodingCharset = UTF-8
* </pre>
* </p>
*
* @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 String originalRequestMethodParameterName = "original_request_method";
private String originalRequestParametersNameValueSeparator = "__";
private String originalRequestParametersParameterName = "original_request_parameters";
private String originalRequestParametersSeparator = ";;";
private String originalRequestUrlParameterName = "original_requestUrl";
private String urlEncodingCharset = "UTF-8";
private boolean forceHttps = false;
private boolean includeOriginalParameters = false;
private boolean includeOriginalRequest = true;
private boolean isOutsideWebApp = false;
//~ Methods ========================================================================================================
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);
}
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);
}
}
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());
}
/**
*
* @return the captcha test page to redirect to.
*/
public String getCaptchaFormUrl() {
return captchaFormUrl;
}
public boolean getForceHttps() {
return forceHttps;
}
public String getOriginalRequestMethodParameterName() {
return originalRequestMethodParameterName;
}
public String getOriginalRequestParametersNameValueSeparator() {
return originalRequestParametersNameValueSeparator;
}
public String getOriginalRequestParametersParameterName() {
return originalRequestParametersParameterName;
}
public String getOriginalRequestParametersSeparator() {
return originalRequestParametersSeparator;
}
public String getOriginalRequestUrlParameterName() {
return originalRequestUrlParameterName;
}
public PortMapper getPortMapper() {
return portMapper;
}
public PortResolver getPortResolver() {
return portResolver;
}
public String getUrlEncodingCharset() {
return urlEncodingCharset;
}
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);
}
}
}
public boolean isIncludeOriginalParameters() {
return includeOriginalParameters;
}
public boolean isIncludeOriginalRequest() {
return includeOriginalRequest;
}
public boolean isOutsideWebApp() {
return isOutsideWebApp;
}
/**
* 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 captchaFormUrl) {
this.captchaFormUrl = captchaFormUrl;
}
// ~ 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 void setIncludeOriginalParameters(boolean includeOriginalParameters) {
this.includeOriginalParameters = includeOriginalParameters;
}
/**
* If set to true, the original request url will be appended to the redirect url using the {@link
* #getOriginalRequestUrlParameterName()}.
*
* @param includeOriginalRequest
*/
public void setIncludeOriginalRequest(boolean includeOriginalRequest) {
this.includeOriginalRequest = includeOriginalRequest;
}
public void setOriginalRequestMethodParameterName(String originalRequestMethodParameterName) {
this.originalRequestMethodParameterName = originalRequestMethodParameterName;
}
public void setOriginalRequestParametersNameValueSeparator(String originalRequestParametersNameValueSeparator) {
this.originalRequestParametersNameValueSeparator = originalRequestParametersNameValueSeparator;
}
public void setOriginalRequestParametersParameterName(String originalRequestParametersParameterName) {
this.originalRequestParametersParameterName = originalRequestParametersParameterName;
}
public void setOriginalRequestParametersSeparator(String originalRequestParametersSeparator) {
this.originalRequestParametersSeparator = originalRequestParametersSeparator;
}
public void setOriginalRequestUrlParameterName(String originalRequestUrlParameterName) {
this.originalRequestUrlParameterName = originalRequestUrlParameterName;
}
/**
* 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 void setPortMapper(PortMapper portMapper) {
this.portMapper = portMapper;
}
public void setPortResolver(PortResolver portResolver) {
this.portResolver = portResolver;
}
public void setUrlEncodingCharset(String urlEncodingCharset) {
this.urlEncodingCharset = urlEncodingCharset;
}
}
@@ -0,0 +1,59 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.captcha;
import org.springframework.security.context.SecurityContext;
/**
* Interface that add humanity concerns to the SecurityContext
*
* @author marc antoine garrigue
*/
public interface CaptchaSecurityContext extends SecurityContext {
//~ Methods ========================================================================================================
/**
* DOCUMENT ME!
*
* @return number of human restricted resources requests since the last passed captcha.
*/
int getHumanRestrictedResourcesRequestsCount();
/**
* DOCUMENT ME!
*
* @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 incrementHumanRestrictedResourcesRequestsCount();
/**
* DOCUMENT ME!
*
* @return true if the current user has already passed a captcha.
*/
boolean isHuman();
/**
* set human attribute, should called after captcha validation.
*/
void setHuman();
}
@@ -0,0 +1,105 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.captcha;
import org.springframework.security.context.SecurityContextImpl;
/**
* Default CaptchaSecurityContext implementation
*
* @author mag
*/
public class CaptchaSecurityContextImpl extends SecurityContextImpl implements CaptchaSecurityContext {
//~ Instance fields ================================================================================================
private boolean human;
private int humanRestrictedResourcesRequestsCount;
private long lastPassedCaptchaDate;
//~ Constructors ===================================================================================================
public CaptchaSecurityContextImpl() {
super();
human = false;
lastPassedCaptchaDate = 0;
humanRestrictedResourcesRequestsCount = 0;
}
//~ Methods ========================================================================================================
public boolean equals(Object obj) {
if (obj instanceof CaptchaSecurityContextImpl) {
CaptchaSecurityContextImpl rhs = (CaptchaSecurityContextImpl) obj;
if (this.isHuman() != rhs.isHuman()) {
return false;
}
if (this.getHumanRestrictedResourcesRequestsCount() != rhs.getHumanRestrictedResourcesRequestsCount()) {
return false;
}
if (this.getLastPassedCaptchaDateInMillis() != rhs.getLastPassedCaptchaDateInMillis()) {
return false;
}
return super.equals(obj);
}
return false;
}
public int getHumanRestrictedResourcesRequestsCount() {
return humanRestrictedResourcesRequestsCount;
}
public long getLastPassedCaptchaDateInMillis() {
return lastPassedCaptchaDate;
}
public int hashCode() {
int code = super.hashCode();
code ^= this.humanRestrictedResourcesRequestsCount;
code ^= this.lastPassedCaptchaDate;
if (this.isHuman()) {
code ^= -37;
}
return code;
}
/**
* Method to increment the human Restricted Resrouces Requests Count;
*/
public void incrementHumanRestrictedResourcesRequestsCount() {
humanRestrictedResourcesRequestsCount++;
}
public boolean isHuman() {
return human;
}
/**
* Reset the lastPassedCaptchaDate and count.
*/
public void setHuman() {
this.human = true;
this.lastPassedCaptchaDate = System.currentTimeMillis();
this.humanRestrictedResourcesRequestsCount = 0;
}
}
@@ -0,0 +1,36 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.captcha;
/**
* Provide a common interface for captcha validation.
*
* @author marc antoine Garrigue
* @version $Id$
*/
public interface CaptchaServiceProxy {
//~ Methods ========================================================================================================
/**
* DOCUMENT ME!
*
* @param id the id token
* @param captchaResponse the user response
*
* @return true if the response is validated by the back end captcha service.
*/
boolean validateReponseForId(String id, Object captchaResponse);
}
@@ -0,0 +1,137 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.captcha;
import org.springframework.security.context.SecurityContextHolder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* 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>
* This Filter should be placed after the ContextIntegration filter and before the {@link
* CaptchaChannelProcessorTemplate} filter in the filter stack in order to update the {@link CaptchaSecurityContext}
* before the humanity verification routine occurs. <br>
* This filter should only be used in conjunction with the {@link CaptchaSecurityContext}<br>
*
* @author marc antoine Garrigue
* @version $Id$
*/
public class CaptchaValidationProcessingFilter implements InitializingBean, 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 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 captchaResponse = request.getParameter(captchaValidationParameter);
if ((request != null) && request instanceof HttpServletRequest && (captchaResponse != 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, captchaResponse);
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");
}
if (logger.isDebugEnabled()) {
logger.debug("chain ...");
}
chain.doFilter(request, response);
}
public CaptchaServiceProxy getCaptchaService() {
return captchaService;
}
public String getCaptchaValidationParameter() {
return captchaValidationParameter;
}
/**
* Does nothing. We use IoC container lifecycle services instead.
*
* @param filterConfig ignored
*
* @throws ServletException ignored
*/
public void init(FilterConfig filterConfig) throws ServletException {}
public void setCaptchaService(CaptchaServiceProxy captchaService) {
this.captchaService = captchaService;
}
public void setCaptchaValidationParameter(String captchaValidationParameter) {
this.captchaValidationParameter = captchaValidationParameter;
}
}
@@ -0,0 +1,50 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.captcha;
/**
* <p>return false if ny CaptchaChannelProcessorTemplate mapped urls has been requested more than thresold and
* humanity is false; <br>
* Default keyword : REQUIRES_CAPTCHA_ONCE_ABOVE_THRESOLD_REQUESTS</p>
*
* @author Marc-Antoine Garrigue
* @version $Id$
*/
public class TestOnceAfterMaxRequestsCaptchaChannelProcessor extends CaptchaChannelProcessorTemplate {
//~ Static fields/initializers =====================================================================================
public static final String DEFAULT_KEYWORD = "REQUIRES_CAPTCHA_ONCE_ABOVE_THRESOLD_REQUESTS";
//~ Constructors ===================================================================================================
public TestOnceAfterMaxRequestsCaptchaChannelProcessor() {
this.setKeyword(DEFAULT_KEYWORD);
}
//~ Methods ========================================================================================================
boolean isContextValidConcerningHumanity(CaptchaSecurityContext context) {
if (context.isHuman() || (context.getHumanRestrictedResourcesRequestsCount() < getThreshold())) {
logger.debug("context is valid concerning humanity or request count < thresold");
return true;
} else {
logger.debug("context is not valid concerning humanity and request count > thresold");
return false;
}
}
}
@@ -0,0 +1,12 @@
<html>
<body>
Captcha classes. Contains :<br/>
<ul>
<li>a CaptchaSecurityContext that overrides the default SecurityContext and holds some captcha related informations</li>
<li>an abstract CaptchaChannelProcessorTemplate and its implementations that test this context according to the configuration</li>
<li>a CaptchaServiceProxy and a CaptchaValidationProcessingFilter that alows to validate a captcha response and to update the CaptchaSecurity</li>
<li>a CaptchaEntryPoint that redirects to a captcha page if the CaptchaChannelProcessor implementation decide so</li>
</ul>
</body>
</html>
@@ -0,0 +1,65 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.captcha;
import junit.framework.TestCase;
/**
*
* @author $author$
* @version $Revision: 2142 $
*/
public class AlwaysTestAfterMaxRequestsCaptchaChannelProcessorTests extends TestCase {
//~ Instance fields ================================================================================================
AlwaysTestAfterMaxRequestsCaptchaChannelProcessor alwaysTestAfterMaxRequestsCaptchaChannelProcessor;
//~ Methods ========================================================================================================
protected void setUp() throws Exception {
super.setUp();
alwaysTestAfterMaxRequestsCaptchaChannelProcessor = new AlwaysTestAfterMaxRequestsCaptchaChannelProcessor();
}
public void testIsContextValidConcerningHumanity()
throws Exception {
alwaysTestAfterMaxRequestsCaptchaChannelProcessor.setThreshold(1);
CaptchaSecurityContextImpl context = new CaptchaSecurityContextImpl();
assertTrue(alwaysTestAfterMaxRequestsCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
context.incrementHumanRestrictedResourcesRequestsCount();
alwaysTestAfterMaxRequestsCaptchaChannelProcessor.setThreshold(-1);
assertFalse(alwaysTestAfterMaxRequestsCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
alwaysTestAfterMaxRequestsCaptchaChannelProcessor.setThreshold(3);
assertTrue(alwaysTestAfterMaxRequestsCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
context.incrementHumanRestrictedResourcesRequestsCount();
assertTrue(alwaysTestAfterMaxRequestsCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
context.incrementHumanRestrictedResourcesRequestsCount();
assertFalse(alwaysTestAfterMaxRequestsCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
}
public void testNewContext() {
CaptchaSecurityContextImpl context = new CaptchaSecurityContextImpl();
assertFalse(alwaysTestAfterMaxRequestsCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
alwaysTestAfterMaxRequestsCaptchaChannelProcessor.setThreshold(1);
assertTrue(alwaysTestAfterMaxRequestsCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
}
}
@@ -0,0 +1,84 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.captcha;
import junit.framework.*;
import org.springframework.security.captcha.AlwaysTestAfterTimeInMillisCaptchaChannelProcessor;
/**
* WARNING! This test class make some assumptions concerning the compute speed! For example the two following
* instructions should be computed in the same millis or the test is not valid.<pre><code>context.setHuman();
* assertFalse(alwaysTestAfterTimeInMillisCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
* </code></pre>This should be the case for most environements unless
* <ul>
* <li>you run it on a good old TRS-80</li>
* <li>you start M$office during this test ;)</li>
* </ul>
*/
public class AlwaysTestAfterTimeInMillisCaptchaChannelProcessorTests extends TestCase {
//~ Instance fields ================================================================================================
AlwaysTestAfterTimeInMillisCaptchaChannelProcessor alwaysTestAfterTimeInMillisCaptchaChannelProcessor;
//~ Methods ========================================================================================================
protected void setUp() throws Exception {
super.setUp();
alwaysTestAfterTimeInMillisCaptchaChannelProcessor = new AlwaysTestAfterTimeInMillisCaptchaChannelProcessor();
}
public void testEqualsThresold() {
CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
assertFalse(alwaysTestAfterTimeInMillisCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
//the two following instructions should be computed or the test is not valid (never fails). This should be the case
// for most environements unless if you run it on a good old TRS-80 (thanks mom).
context.setHuman();
assertFalse(alwaysTestAfterTimeInMillisCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
}
/* Commented out as it makes assumptions about the speed of the build server and fails intermittently on
build.springframework.org - L.T.
public void testIsContextValidConcerningHumanity()
throws Exception {
CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
alwaysTestAfterTimeInMillisCaptchaChannelProcessor.setThreshold(100);
context.setHuman();
while ((System.currentTimeMillis() - context.getLastPassedCaptchaDateInMillis()) < alwaysTestAfterTimeInMillisCaptchaChannelProcessor
.getThreshold()) {
assertTrue(alwaysTestAfterTimeInMillisCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
context.incrementHumanRestrictedRessoucesRequestsCount();
long now = System.currentTimeMillis();
while ((System.currentTimeMillis() - now) < 1) {}
;
}
assertFalse(alwaysTestAfterTimeInMillisCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
}
*/
public void testNewContext() {
CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
//alwaysTestAfterTimeInMillisCaptchaChannelProcessor.setThreshold(10);
assertFalse(alwaysTestAfterTimeInMillisCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
}
}
@@ -0,0 +1,117 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.captcha;
import junit.framework.TestCase;
/**
* DOCUMENT ME!
*
* @author $author$
* @version $Revision: 2142 $
*/
public class AlwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessorTests extends TestCase {
//~ Instance fields ================================================================================================
AlwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor;
//~ Methods ========================================================================================================
protected void setUp() throws Exception {
super.setUp();
alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor = new AlwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor();
}
public void testEqualsThresold() {
CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor.setThreshold(100);
context.setHuman();
long now = System.currentTimeMillis();
/*
while ((System.currentTimeMillis() - now) <= 100) {
assertTrue(alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor
.isContextValidConcerningHumanity(context));
}
context.incrementHumanRestrictedRessoucesRequestsCount();
assertTrue(alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor.isContextValidConcerningHumanity(
context));
context.setHuman();
context.incrementHumanRestrictedRessoucesRequestsCount();
assertFalse(alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor.isContextValidConcerningHumanity(
context));
alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor.setThreshold(0);
context.setHuman();
context.incrementHumanRestrictedRessoucesRequestsCount();
assertFalse(alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor.isContextValidConcerningHumanity(
context));
alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor.setThreshold(0);
*/
}
/*
public void testIsContextValidConcerningHumanity()
throws Exception {
CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor.setThreshold(10);
context.setHuman();
while ((System.currentTimeMillis() - context.getLastPassedCaptchaDateInMillis()) < (10 * alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor
.getThreshold())) {
assertTrue(alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor
.isContextValidConcerningHumanity(context));
}
}
public void testNewContext() {
CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
assertFalse(alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor.isContextValidConcerningHumanity(
context));
context.setHuman();
assertTrue(alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor.isContextValidConcerningHumanity(
context));
}
public void testShouldPassAbove() {
CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
context.setHuman();
int i = 0;
while ((System.currentTimeMillis() - context.getLastPassedCaptchaDateInMillis()) < (100 * alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor
.getThreshold())) {
System.out.println((System.currentTimeMillis() - context.getLastPassedCaptchaDateInMillis()));
context.incrementHumanRestrictedRessoucesRequestsCount();
i++;
while ((System.currentTimeMillis() - context.getLastPassedCaptchaDateInMillis()) < (alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor
.getThreshold() * i)) {}
System.out.println((System.currentTimeMillis() - context.getLastPassedCaptchaDateInMillis()));
assertTrue(alwaysTestBelowAverageTimeInMillisBetweenRequestsChannelProcessor
.isContextValidConcerningHumanity(context));
}
}
*/
}
@@ -0,0 +1,229 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.captcha;
import junit.framework.TestCase;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.MockFilterChain;
import org.springframework.security.SecurityConfig;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.intercept.web.FilterInvocation;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.FilterChain;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* Tests {@link org.springframework.security.captcha.CaptchaChannelProcessorTemplate}
*
* @author marc antoine Garrigue
* @version $Id$
*/
public class CaptchaChannelProcessorTemplateTests extends TestCase {
//~ Methods ========================================================================================================
private MockHttpServletResponse decideWithNewResponse(ConfigAttributeDefinition cad,
CaptchaChannelProcessorTemplate 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 setUp() {
SecurityContextHolder.clearContext();
}
public void tearDown() {
SecurityContextHolder.clearContext();
}
public void testContextRedirect() throws Exception {
CaptchaChannelProcessorTemplate processor = new TestHumanityCaptchaChannelProcessor();
processor.setKeyword("X");
ConfigAttributeDefinition cad = new ConfigAttributeDefinition("Y");
CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
SecurityContextHolder.setContext(context);
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(null, response.getRedirectedUrl());
processor.setKeyword("Y");
response = decideWithNewResponse(cad, processor, request);
assertEquals("http://localhost:8000/demo/jcaptcha.do", response.getRedirectedUrl());
context.setHuman();
response = decideWithNewResponse(cad, processor, request);
assertEquals(null, response.getRedirectedUrl());
}
public void testDecideRejectsNulls() throws Exception {
CaptchaChannelProcessorTemplate processor = new TestHumanityCaptchaChannelProcessor();
processor.setEntryPoint(new CaptchaEntryPoint());
processor.setKeyword("X");
processor.afterPropertiesSet();
try {
processor.decide(null, null);
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
public void testGettersSetters() {
CaptchaChannelProcessorTemplate processor = new TestHumanityCaptchaChannelProcessor();
assertEquals(null, processor.getKeyword());
processor.setKeyword("X");
assertEquals("X", processor.getKeyword());
assertEquals(0, processor.getThreshold());
processor.setThreshold(1);
assertEquals(1, processor.getThreshold());
assertTrue(processor.getEntryPoint() == null);
processor.setEntryPoint(new CaptchaEntryPoint());
assertTrue(processor.getEntryPoint() != null);
}
public void testIncrementRequestCount() throws Exception {
CaptchaChannelProcessorTemplate processor = new TestHumanityCaptchaChannelProcessor();
processor.setKeyword("X");
ConfigAttributeDefinition cad = new ConfigAttributeDefinition("X");
CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
SecurityContextHolder.setContext(context);
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(0, context.getHumanRestrictedResourcesRequestsCount());
context.setHuman();
decideWithNewResponse(cad, processor, request);
assertEquals(1, context.getHumanRestrictedResourcesRequestsCount());
decideWithNewResponse(cad, processor, request);
assertEquals(2, context.getHumanRestrictedResourcesRequestsCount());
processor.setKeyword("Y");
decideWithNewResponse(cad, processor, request);
assertEquals(2, context.getHumanRestrictedResourcesRequestsCount());
context = new CaptchaSecurityContextImpl();
decideWithNewResponse(cad, processor, request);
assertEquals(0, context.getHumanRestrictedResourcesRequestsCount());
}
public void testMissingEntryPoint() throws Exception {
CaptchaChannelProcessorTemplate processor = new TestHumanityCaptchaChannelProcessor();
processor.setEntryPoint(null);
try {
processor.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("entryPoint required", expected.getMessage());
}
}
public void testMissingKeyword() throws Exception {
CaptchaChannelProcessorTemplate processor = new TestHumanityCaptchaChannelProcessor();
processor.setKeyword(null);
try {
processor.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {}
processor.setKeyword("");
try {
processor.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {}
}
public void testSupports() {
CaptchaChannelProcessorTemplate processor = new TestHumanityCaptchaChannelProcessor();
processor.setKeyword("X");
assertTrue(processor.supports(new SecurityConfig(processor.getKeyword())));
assertTrue(processor.supports(new SecurityConfig("X")));
assertFalse(processor.supports(null));
assertFalse(processor.supports(new SecurityConfig("NOT_SUPPORTED")));
}
//~ Inner Classes ==================================================================================================
private class TestHumanityCaptchaChannelProcessor extends CaptchaChannelProcessorTemplate {
boolean isContextValidConcerningHumanity(CaptchaSecurityContext context) {
return context.isHuman();
}
}
private static class MockFilterChain implements FilterChain {
public void doFilter(ServletRequest arg0, ServletResponse arg1) throws IOException, ServletException {
throw new UnsupportedOperationException("mock method not implemented");
}
}
}
@@ -0,0 +1,386 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.captcha;
import junit.framework.TestCase;
import org.springframework.security.MockPortResolver;
import org.springframework.security.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 CaptchaEntryPoint}.
*
* @author marc antoine Garrigue
* @version $Id$
*/
public class CaptchaEntryPointTests extends TestCase {
//~ Methods ========================================================================================================
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());
}
}
@@ -0,0 +1,106 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.captcha;
import junit.framework.TestCase;
/**
* Tests {@link CaptchaSecurityContextImpl}.
*
* @author marc antoine Garrigue
* @version $Id$
*/
public class CaptchaSecurityContextImplTests extends TestCase {
//~ Methods ========================================================================================================
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 testEquals() {
CaptchaSecurityContext context1 = new CaptchaSecurityContextImpl();
CaptchaSecurityContext context2 = new CaptchaSecurityContextImpl();
assertEquals(context1, context2);
assertFalse(context1.isHuman());
context1.setHuman();
assertNotSame(context1, context2);
// Get fresh copy
context1 = new CaptchaSecurityContextImpl();
assertEquals(context1, context2);
context1.incrementHumanRestrictedResourcesRequestsCount();
assertNotSame(context1, context2);
}
public void testHashcode() {
CaptchaSecurityContext context1 = new CaptchaSecurityContextImpl();
CaptchaSecurityContext context2 = new CaptchaSecurityContextImpl();
assertEquals(context1.hashCode(), context2.hashCode());
assertFalse(context1.isHuman());
context1.setHuman();
assertTrue(context1.hashCode() != context2.hashCode());
// Get fresh copy
context1 = new CaptchaSecurityContextImpl();
assertEquals(context1.hashCode(), context2.hashCode());
context1.incrementHumanRestrictedResourcesRequestsCount();
assertTrue(context1 != context2);
}
public void testIncrementRequests() {
CaptchaSecurityContext context = new CaptchaSecurityContextImpl();
context.setHuman();
assertEquals("should be human", true, context.isHuman());
assertEquals("should be 0", 0, context.getHumanRestrictedResourcesRequestsCount());
context.incrementHumanRestrictedResourcesRequestsCount();
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.incrementHumanRestrictedResourcesRequestsCount();
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 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());
}
}
@@ -0,0 +1,114 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.captcha;
import junit.framework.TestCase;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.util.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
/**
* Tests {@link CaptchaValidationProcessingFilter}.
*
* @author marc antoine Garrigue
* @version $Id$
*/
public class CaptchaValidationProcessingFilterTests extends TestCase {
//~ Methods ========================================================================================================
/*
*/
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
* 'org.springframework.security.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());
}
/*
* Test method for
* 'org.springframework.security.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());
}
}
@@ -0,0 +1,37 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.captcha;
/**
* DOCUMENT ME!
*
* @author marc antoine Garrigue
* @version $Id$
*/
public class MockCaptchaServiceProxy implements CaptchaServiceProxy {
//~ Instance fields ================================================================================================
public boolean hasBeenCalled = false;
public boolean valid = false;
//~ Methods ========================================================================================================
public boolean validateReponseForId(String id, Object response) {
hasBeenCalled = true;
return valid;
}
}
@@ -0,0 +1,73 @@
/* Copyright 2004, 2005, 2006 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 org.springframework.security.captcha;
import junit.framework.*;
import org.springframework.security.captcha.TestOnceAfterMaxRequestsCaptchaChannelProcessor;
/**
* DOCUMENT ME!
*
* @author $author$
* @version $Revision: 2142 $
*/
public class TestOnceAfterMaxRequestsCaptchaChannelProcessorTests extends TestCase {
//~ Instance fields ================================================================================================
TestOnceAfterMaxRequestsCaptchaChannelProcessor testOnceAfterMaxRequestsCaptchaChannelProcessor;
//~ Methods ========================================================================================================
protected void setUp() throws Exception {
super.setUp();
testOnceAfterMaxRequestsCaptchaChannelProcessor = new TestOnceAfterMaxRequestsCaptchaChannelProcessor();
}
public void testIsContextValidConcerningHumanity()
throws Exception {
testOnceAfterMaxRequestsCaptchaChannelProcessor.setThreshold(1);
CaptchaSecurityContextImpl context = new CaptchaSecurityContextImpl();
assertTrue(testOnceAfterMaxRequestsCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
context.incrementHumanRestrictedResourcesRequestsCount();
testOnceAfterMaxRequestsCaptchaChannelProcessor.setThreshold(-1);
assertFalse(testOnceAfterMaxRequestsCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
testOnceAfterMaxRequestsCaptchaChannelProcessor.setThreshold(3);
assertTrue(testOnceAfterMaxRequestsCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
context.incrementHumanRestrictedResourcesRequestsCount();
assertTrue(testOnceAfterMaxRequestsCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
context.incrementHumanRestrictedResourcesRequestsCount();
assertFalse(testOnceAfterMaxRequestsCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
context.setHuman();
for (int i = 0; i < (2 * testOnceAfterMaxRequestsCaptchaChannelProcessor.getThreshold()); i++) {
assertTrue(testOnceAfterMaxRequestsCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
}
}
public void testNewContext() {
CaptchaSecurityContextImpl context = new CaptchaSecurityContextImpl();
assertFalse(testOnceAfterMaxRequestsCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
testOnceAfterMaxRequestsCaptchaChannelProcessor.setThreshold(1);
assertTrue(testOnceAfterMaxRequestsCaptchaChannelProcessor.isContextValidConcerningHumanity(context));
}
}