1
0
mirror of synced 2026-08-05 09:47:05 +00:00

Significantly enhance channel processing filter.

This commit is contained in:
Ben Alex
2004-04-27 06:21:00 +00:00
parent e555d77d4e
commit 901c7d4752
17 changed files with 1542 additions and 145 deletions
@@ -29,10 +29,11 @@ public interface ChannelDecisionManager {
//~ Methods ================================================================
/**
* Decided whether the presented {@link FilterInvocation} provides
* sufficient security based on the requested {@link
* Decided whether the presented {@link FilterInvocation} provides the
* appropriate level of channel security based on the requested {@link
* ConfigAttributeDefinition}.
*/
public void decide(FilterInvocation invocation,
ConfigAttributeDefinition config) throws SecureChannelRequiredException;
ConfigAttributeDefinition config)
throws InsecureChannelRequiredException, SecureChannelRequiredException;
}
@@ -26,14 +26,31 @@ import java.util.Iterator;
/**
* <p>
* Requires a secure channel for a web request if a {@link
* ConfigAttribute#getAttribute()} keyword is detected.
* Ensures configuration attribute requested channel security is present by
* review of <code>HttpServletRequest.isSecure()</code> responses.
* </p>
*
* <P>
* The default keyword string is <Code>REQUIRES_SECURE_CHANNEL</code>, but this
* may be overriden to any value. The <code>ConfigAttribute</code> must
* exactly match the case of the keyword string.
* The class responds to two and only two case-sensitive keywords: {@link
* #getInsecureKeyword()} and {@link #getSecureKeyword}. If either of these
* keywords are detected, <code>HttpServletRequest.isSecure()</code> is used
* to determine the channel security offered. If the channel security differs
* from that requested by the keyword, the relevant exception is thrown.
* </p>
*
* <P>
* If both the <code>secureKeyword</code> and <code>insecureKeyword</code>
* configuration attributes are detected, the request will be deemed to be
* requesting a secure channel. This is a reasonable approach, as when in
* doubt, the decision manager assumes the most secure outcome is desired. Of
* course, you <b>should</b> indicate one configuration attribute or the other
* (not both).
* </p>
*
* <P>
* The default <code>secureKeyword</code> and <code>insecureKeyword</code> is
* <code>REQUIRES_SECURE_CHANNEL</code> and
* <code>REQUIRES_INSECURE_CHANNEL</code> respectively.
* </p>
*
* @author Ben Alex
@@ -43,21 +60,34 @@ public class ChannelDecisionManagerImpl implements InitializingBean,
ChannelDecisionManager {
//~ Instance fields ========================================================
private String keyword = "REQUIRES_SECURE_CHANNEL";
private String insecureKeyword = "REQUIRES_INSECURE_CHANNEL";
private String secureKeyword = "REQUIRES_SECURE_CHANNEL";
//~ Methods ================================================================
public void setKeyword(String keyword) {
this.keyword = keyword;
public void setInsecureKeyword(String insecureKeyword) {
this.insecureKeyword = insecureKeyword;
}
public String getKeyword() {
return keyword;
public String getInsecureKeyword() {
return insecureKeyword;
}
public void setSecureKeyword(String secureKeyword) {
this.secureKeyword = secureKeyword;
}
public String getSecureKeyword() {
return secureKeyword;
}
public void afterPropertiesSet() throws Exception {
if ((keyword == null) || "".equals(keyword)) {
throw new IllegalArgumentException("keyword required");
if ((secureKeyword == null) || "".equals(secureKeyword)) {
throw new IllegalArgumentException("secureKeyword required");
}
if ((insecureKeyword == null) || "".equals(insecureKeyword)) {
throw new IllegalArgumentException("insecureKeyword required");
}
}
@@ -72,12 +102,19 @@ public class ChannelDecisionManagerImpl implements InitializingBean,
while (iter.hasNext()) {
ConfigAttribute attribute = (ConfigAttribute) iter.next();
if (attribute.equals(keyword)) {
if (attribute.equals(secureKeyword)) {
if (!invocation.getHttpRequest().isSecure()) {
throw new SecureChannelRequiredException(
"Request is not being made over a secure channel");
}
}
if (attribute.equals(insecureKeyword)) {
if (invocation.getHttpRequest().isSecure()) {
throw new InsecureChannelRequiredException(
"Request is being made over a secure channel when an insecure channel is required");
}
}
}
}
}
@@ -23,7 +23,12 @@ import javax.servlet.ServletResponse;
/**
* Used by {@link ChannelProcessingFilter} to launch a secure web channel.
* Used by {@link ChannelProcessingFilter} to launch a web channel.
*
* <P>
* Depending on the implementation, a secure or insecure channel will be
* launched.
* </p>
*
* @author Ben Alex
* @version $Id$
@@ -37,12 +42,14 @@ public interface ChannelEntryPoint {
* <P>
* Implementations should modify the headers on the
* <code>ServletResponse</code> as necessary to commence the user agent
* using the secure channel.
* using the implementation's supported channel type (ie secure or
* insecure).
* </p>
*
* @param request that resulted in a
* <code>SecureChannelRequiredException</code>
* @param response so that the user agent can begin using a secure channel
* <code>SecureChannelRequiredException</code> or
* <code>InsecureChannelRequiredException</code>
* @param response so that the user agent can begin using a new channel
*/
public void commence(ServletRequest request, ServletResponse response)
throws IOException, ServletException;
@@ -37,7 +37,7 @@ import javax.servlet.http.HttpServletResponse;
/**
* Ensures a request is delivered over a secure channel.
* Ensures a web request is delivered over the required channel.
*
* <p>
* Internally uses a {@link FilterInvocation} to represent the request, so that
@@ -62,7 +62,8 @@ public class ChannelProcessingFilter implements InitializingBean, Filter {
//~ Instance fields ========================================================
private ChannelDecisionManager channelDecisionManager;
private ChannelEntryPoint channelEntryPoint;
private ChannelEntryPoint insecureChannelEntryPoint;
private ChannelEntryPoint secureChannelEntryPoint;
private FilterInvocationDefinitionSource filterInvocationDefinitionSource;
//~ Methods ================================================================
@@ -76,14 +77,6 @@ public class ChannelProcessingFilter implements InitializingBean, Filter {
return channelDecisionManager;
}
public void setChannelEntryPoint(ChannelEntryPoint channelEntryPoint) {
this.channelEntryPoint = channelEntryPoint;
}
public ChannelEntryPoint getChannelEntryPoint() {
return channelEntryPoint;
}
public void setFilterInvocationDefinitionSource(
FilterInvocationDefinitionSource filterInvocationDefinitionSource) {
this.filterInvocationDefinitionSource = filterInvocationDefinitionSource;
@@ -93,6 +86,23 @@ public class ChannelProcessingFilter implements InitializingBean, Filter {
return filterInvocationDefinitionSource;
}
public void setInsecureChannelEntryPoint(
ChannelEntryPoint insecureChannelEntryPoint) {
this.insecureChannelEntryPoint = insecureChannelEntryPoint;
}
public ChannelEntryPoint getInsecureChannelEntryPoint() {
return insecureChannelEntryPoint;
}
public void setSecureChannelEntryPoint(ChannelEntryPoint channelEntryPoint) {
this.secureChannelEntryPoint = channelEntryPoint;
}
public ChannelEntryPoint getSecureChannelEntryPoint() {
return secureChannelEntryPoint;
}
public void afterPropertiesSet() throws Exception {
if (filterInvocationDefinitionSource == null) {
throw new IllegalArgumentException(
@@ -104,9 +114,14 @@ public class ChannelProcessingFilter implements InitializingBean, Filter {
"channelDecisionManager must be specified");
}
if (channelEntryPoint == null) {
if (secureChannelEntryPoint == null) {
throw new IllegalArgumentException(
"channelEntryPoint must be specified");
"secureChannelEntryPoint must be specified");
}
if (insecureChannelEntryPoint == null) {
throw new IllegalArgumentException(
"insecureChannelEntryPoint must be specified");
}
}
@@ -128,20 +143,32 @@ public class ChannelProcessingFilter implements InitializingBean, Filter {
if (attr != null) {
if (logger.isDebugEnabled()) {
logger.debug("Request : " + request.toString()
logger.debug("Request: " + fi.getFullRequestUrl()
+ "; ConfigAttributes: " + attr.toString());
}
try {
channelDecisionManager.decide(fi, attr);
} catch (SecureChannelRequiredException channelException) {
} catch (SecureChannelRequiredException secureException) {
if (logger.isDebugEnabled()) {
logger.debug("Channel insufficient ("
+ channelException.getMessage()
+ "); delegating to channelEntryPoint");
logger.debug("Channel insufficient security ("
+ secureException.getMessage()
+ "); delegating to secureChannelEntryPoint");
}
channelEntryPoint.commence(request, response);
secureChannelEntryPoint.commence(request, response);
return;
} catch (InsecureChannelRequiredException insecureException) {
if (logger.isDebugEnabled()) {
logger.debug("Channel too much security ("
+ insecureException.getMessage()
+ "); delegating to insecureChannelEntryPoint");
}
insecureChannelEntryPoint.commence(request, response);
return;
}
}
@@ -0,0 +1,50 @@
/* 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.securechannel;
import net.sf.acegisecurity.AccessDeniedException;
/**
* Thrown if a secure web channel is detected, but is not required.
*
* @author Ben Alex
* @version $Id$
*/
public class InsecureChannelRequiredException extends AccessDeniedException {
//~ Constructors ===========================================================
/**
* Constructs an <code>InsecureChannelRequiredException</code> with the
* specified message.
*
* @param msg the detail message.
*/
public InsecureChannelRequiredException(String msg) {
super(msg);
}
/**
* Constructs an <code>InsecureChannelRequiredException</code> with the
* specified message and root cause.
*
* @param msg the detail message.
* @param t root cause
*/
public InsecureChannelRequiredException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -0,0 +1,120 @@
/* 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.securechannel;
import net.sf.acegisecurity.util.PortMapper;
import net.sf.acegisecurity.util.PortResolver;
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.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Commences an insecure channel by retrying the original request using HTTP.
*
* <P>
* This entry point should suffice in most circumstances. However, it is not
* intended to properly handle HTTP POSTs or other usage where a standard
* redirect would cause an issue.
* </p>
*
* @author Ben Alex
* @version $Id$
*/
public class RetryWithHttpEntryPoint implements InitializingBean,
ChannelEntryPoint {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(RetryWithHttpEntryPoint.class);
//~ Instance fields ========================================================
private PortMapper portMapper;
private PortResolver portResolver;
//~ Methods ================================================================
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 void afterPropertiesSet() throws Exception {
if (portMapper == null) {
throw new IllegalArgumentException("portMapper is required");
}
if (portResolver == null) {
throw new IllegalArgumentException("portResolver is required");
}
}
public void commence(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String pathInfo = req.getPathInfo();
String queryString = req.getQueryString();
String contextPath = req.getContextPath();
String destination = req.getServletPath()
+ ((pathInfo == null) ? "" : pathInfo)
+ ((queryString == null) ? "" : ("?" + queryString));
String redirectUrl = contextPath;
Integer httpsPort = new Integer(portResolver.getServerPort(req));
Integer httpPort = portMapper.lookupHttpPort(httpsPort);
if (httpPort != null) {
boolean includePort = true;
if (httpPort.intValue() == 80) {
includePort = false;
}
redirectUrl = "http://" + req.getServerName()
+ ((includePort) ? (":" + httpPort) : "") + contextPath
+ destination;
}
if (logger.isDebugEnabled()) {
logger.debug("Redirecting to: " + redirectUrl);
}
((HttpServletResponse) response).sendRedirect(redirectUrl);
}
}
@@ -15,6 +15,9 @@
package net.sf.acegisecurity.securechannel;
import net.sf.acegisecurity.util.PortMapper;
import net.sf.acegisecurity.util.PortResolver;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -22,10 +25,6 @@ import org.springframework.beans.factory.InitializingBean;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
@@ -53,71 +52,34 @@ public class RetryWithHttpsEntryPoint implements InitializingBean,
//~ Instance fields ========================================================
private Map httpsPortMappings;
//~ Constructors ===========================================================
public RetryWithHttpsEntryPoint() {
httpsPortMappings = new HashMap();
httpsPortMappings.put(new Integer(80), new Integer(443));
httpsPortMappings.put(new Integer(8080), new Integer(8443));
}
private PortMapper portMapper;
private PortResolver portResolver;
//~ Methods ================================================================
/**
* <p>
* Set to override the default http port to https port mappings of 80:443,
* and 8080:8443.
* </p>
* In a Spring XML ApplicationContext, a definition would look something
* like this:
* <pre>
* &lt;property name="httpsPortMapping">
* &lt;map>
* &lt;entry key="80">&lt;value>443&lt;/value>&lt;/entry>
* &lt;entry key="8080">&lt;value>8443&lt;/value>&lt;/entry>
* &lt;/map>
* &lt;/property>
* </pre>
*
* @param newMappings A Map consisting of String keys and String values,
* where for each entry the key is the string representation of an
* integer http port number, and the value is the string
* representation of the corresponding integer https port number.
*
* @throws IllegalArgumentException if input map does not consist of String
* keys and values, each representing an integer port number in
* the range 1-65535 for that mapping.
*/
public void setHttpsPortMappings(HashMap newMappings) {
httpsPortMappings.clear();
public void setPortMapper(PortMapper portMapper) {
this.portMapper = portMapper;
}
Iterator it = newMappings.entrySet().iterator();
public PortMapper getPortMapper() {
return portMapper;
}
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
Integer httpPort = new Integer((String) entry.getKey());
Integer httpsPort = new Integer((String) entry.getValue());
public void setPortResolver(PortResolver portResolver) {
this.portResolver = portResolver;
}
if ((httpPort.intValue() < 1) || (httpPort.intValue() > 65535)
|| (httpsPort.intValue() < 1) || (httpsPort.intValue() > 65535)) {
throw new IllegalArgumentException(
"one or both ports out of legal range: " + httpPort + ", "
+ httpsPort);
}
httpsPortMappings.put(httpPort, httpsPort);
if (httpsPortMappings.size() < 1) {
throw new IllegalArgumentException("must map at least one port");
}
}
public PortResolver getPortResolver() {
return portResolver;
}
public void afterPropertiesSet() throws Exception {
if (httpsPortMappings == null) {
throw new IllegalArgumentException("httpsPortMappings required");
if (portMapper == null) {
throw new IllegalArgumentException("portMapper is required");
}
if (portResolver == null) {
throw new IllegalArgumentException("portResolver is required");
}
}
@@ -134,25 +96,25 @@ public class RetryWithHttpsEntryPoint implements InitializingBean,
String redirectUrl = contextPath;
Integer httpPort = new Integer(req.getServerPort());
Integer httpsPort = (Integer) httpsPortMappings.get(httpPort);
Integer httpPort = new Integer(portResolver.getServerPort(req));
Integer httpsPort = portMapper.lookupHttpsPort(httpPort);
if (httpsPort != null) {
String serverName = req.getServerName();
redirectUrl = "https://" + serverName + ":" + httpsPort
+ contextPath + destination;
boolean includePort = true;
if (httpsPort.intValue() == 443) {
includePort = false;
}
redirectUrl = "https://" + req.getServerName()
+ ((includePort) ? (":" + httpsPort) : "") + contextPath
+ destination;
}
if (logger.isDebugEnabled()) {
logger.debug("Redirecting to: " + redirectUrl);
}
((HttpServletResponse) response).sendRedirect(redirectUrl);
}
/**
* Returns the translated (Integer -> Integer) version of the original port
* mapping specified via setHttpsPortMapping()
*
* @return DOCUMENT ME!
*/
protected Map getTranslatedHttpsPortMappings() {
return httpsPortMappings;
}
}
@@ -0,0 +1,182 @@
/* 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.securechannel;
import junit.framework.TestCase;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import net.sf.acegisecurity.MockFilterChain;
import net.sf.acegisecurity.MockHttpServletRequest;
import net.sf.acegisecurity.MockHttpServletResponse;
import net.sf.acegisecurity.SecurityConfig;
import net.sf.acegisecurity.intercept.web.FilterInvocation;
/**
* Tests {@link ChannelDecisionManagerImpl}.
*
* @author Ben Alex
* @version $Id$
*/
public class ChannelDecisionManagerImplTests extends TestCase {
//~ Methods ================================================================
public final void setUp() throws Exception {
super.setUp();
}
public static void main(String[] args) {
junit.textui.TestRunner.run(ChannelDecisionManagerImplTests.class);
}
public void testDetectsInvalidInsecureKeyword() throws Exception {
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
cdm.setInsecureKeyword("");
try {
cdm.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("insecureKeyword required", expected.getMessage());
}
cdm.setInsecureKeyword(null);
try {
cdm.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("insecureKeyword required", expected.getMessage());
}
}
public void testDetectsInvalidSecureKeyword() throws Exception {
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
cdm.setSecureKeyword("");
try {
cdm.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("secureKeyword required", expected.getMessage());
}
cdm.setSecureKeyword(null);
try {
cdm.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("secureKeyword required", expected.getMessage());
}
}
public void testDetectsNullsPassedToMainMethod() {
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
try {
cdm.decide(null, new ConfigAttributeDefinition());
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("Nulls cannot be provided", expected.getMessage());
}
try {
cdm.decide(new FilterInvocation(new MockHttpServletRequest("x"),
new MockHttpServletResponse(), new MockFilterChain()), null);
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("Nulls cannot be provided", expected.getMessage());
}
}
public void testDetectsWhenInsecureChannelNeededAndInsecureSchemeUsed() {
ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
attr.addConfigAttribute(new SecurityConfig(
"SOME_CONFIG_ATTRIBUTE_TO_IGNORE"));
attr.addConfigAttribute(new SecurityConfig("REQUIRES_INSECURE_CHANNEL"));
MockHttpServletRequest request = new MockHttpServletRequest("foo=bar");
request.setScheme("http");
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
cdm.decide(new FilterInvocation(request, new MockHttpServletResponse(),
new MockFilterChain()), attr);
assertTrue(true);
}
public void testDetectsWhenInsecureChannelNeededAndSecureSchemeUsed() {
ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
attr.addConfigAttribute(new SecurityConfig(
"SOME_CONFIG_ATTRIBUTE_TO_IGNORE"));
attr.addConfigAttribute(new SecurityConfig("REQUIRES_INSECURE_CHANNEL"));
MockHttpServletRequest request = new MockHttpServletRequest("foo=bar");
request.setScheme("https");
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
try {
cdm.decide(new FilterInvocation(request,
new MockHttpServletResponse(), new MockFilterChain()), attr);
} catch (InsecureChannelRequiredException expected) {
assertTrue(true);
}
}
public void testDetectsWhenSecureChannelNeeded() {
ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
attr.addConfigAttribute(new SecurityConfig(
"SOME_CONFIG_ATTRIBUTE_TO_IGNORE"));
attr.addConfigAttribute(new SecurityConfig("REQUIRES_SECURE_CHANNEL"));
MockHttpServletRequest request = new MockHttpServletRequest("foo=bar");
request.setScheme("http");
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
try {
cdm.decide(new FilterInvocation(request,
new MockHttpServletResponse(), new MockFilterChain()), attr);
} catch (SecureChannelRequiredException expected) {
assertTrue(true);
}
}
public void testGetterSetters() throws Exception {
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
cdm.afterPropertiesSet();
assertEquals("REQUIRES_INSECURE_CHANNEL", cdm.getInsecureKeyword());
assertEquals("REQUIRES_SECURE_CHANNEL", cdm.getSecureKeyword());
cdm.setInsecureKeyword("MY_INSECURE");
cdm.setSecureKeyword("MY_SECURE");
assertEquals("MY_INSECURE", cdm.getInsecureKeyword());
assertEquals("MY_SECURE", cdm.getSecureKeyword());
}
public void testIgnoresOtherConfigAttributes() {
ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
attr.addConfigAttribute(new SecurityConfig("XYZ"));
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
cdm.decide(new FilterInvocation(new MockHttpServletRequest("x"),
new MockHttpServletResponse(), new MockFilterChain()), attr);
assertTrue(true);
}
}
;
@@ -0,0 +1,339 @@
/* 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.securechannel;
import junit.framework.TestCase;
import net.sf.acegisecurity.ConfigAttributeDefinition;
import net.sf.acegisecurity.MockFilterConfig;
import net.sf.acegisecurity.MockHttpServletRequest;
import net.sf.acegisecurity.MockHttpServletResponse;
import net.sf.acegisecurity.SecurityConfig;
import net.sf.acegisecurity.intercept.web.FilterInvocation;
import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSource;
import net.sf.acegisecurity.intercept.web.RegExpBasedFilterInvocationDefinitionMap;
import java.io.IOException;
import java.util.Iterator;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* Tests {@link ChannelProcessingFilter}.
*
* @author Ben Alex
* @version $Id$
*/
public class ChannelProcessingFilterTests extends TestCase {
//~ Methods ================================================================
public final void setUp() throws Exception {
super.setUp();
}
public static void main(String[] args) {
junit.textui.TestRunner.run(ChannelProcessingFilterTests.class);
}
public void testCallsInsecureEntryPointWhenTooMuchChannelSecurity()
throws Exception {
ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
attr.addConfigAttribute(new SecurityConfig("REQUIRES_INSECURE_CHANNEL"));
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path",
attr);
ChannelProcessingFilter filter = new ChannelProcessingFilter();
filter.setInsecureChannelEntryPoint(new MockEntryPoint(true));
filter.setSecureChannelEntryPoint(new MockEntryPoint(false));
filter.setFilterInvocationDefinitionSource(fids);
filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
MockHttpServletRequest request = new MockHttpServletRequest("info=now");
request.setServletPath("/path");
request.setScheme("https");
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterChain chain = new MockFilterChain(false);
filter.doFilter(request, response, chain);
assertTrue(true);
}
public void testCallsSecureEntryPointWhenTooLittleChannelSecurity()
throws Exception {
ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
attr.addConfigAttribute(new SecurityConfig("REQUIRES_SECURE_CHANNEL"));
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path",
attr);
ChannelProcessingFilter filter = new ChannelProcessingFilter();
filter.setInsecureChannelEntryPoint(new MockEntryPoint(false));
filter.setSecureChannelEntryPoint(new MockEntryPoint(true));
filter.setFilterInvocationDefinitionSource(fids);
filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
MockHttpServletRequest request = new MockHttpServletRequest("info=now");
request.setServletPath("/path");
request.setScheme("http");
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterChain chain = new MockFilterChain(false);
filter.doFilter(request, response, chain);
assertTrue(true);
}
public void testDetectsMissingChannelDecisionManager()
throws Exception {
ChannelProcessingFilter filter = new ChannelProcessingFilter();
filter.setSecureChannelEntryPoint(new RetryWithHttpsEntryPoint());
filter.setFilterInvocationDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
try {
filter.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("channelDecisionManager must be specified",
expected.getMessage());
}
}
public void testDetectsMissingFilterInvocationDefinitionMap()
throws Exception {
ChannelProcessingFilter filter = new ChannelProcessingFilter();
filter.setInsecureChannelEntryPoint(new RetryWithHttpEntryPoint());
filter.setSecureChannelEntryPoint(new RetryWithHttpsEntryPoint());
filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
try {
filter.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("filterInvocationDefinitionSource must be specified",
expected.getMessage());
}
}
public void testDetectsMissingInsecureChannelEntryPoint()
throws Exception {
ChannelProcessingFilter filter = new ChannelProcessingFilter();
filter.setSecureChannelEntryPoint(new RetryWithHttpsEntryPoint());
filter.setFilterInvocationDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
try {
filter.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("insecureChannelEntryPoint must be specified",
expected.getMessage());
}
}
public void testDetectsMissingSecureChannelEntryPoint()
throws Exception {
ChannelProcessingFilter filter = new ChannelProcessingFilter();
filter.setInsecureChannelEntryPoint(new RetryWithHttpEntryPoint());
filter.setFilterInvocationDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
try {
filter.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("secureChannelEntryPoint must be specified",
expected.getMessage());
}
}
public void testDoFilterWithNonHttpServletRequestDetected()
throws Exception {
ChannelProcessingFilter filter = new ChannelProcessingFilter();
try {
filter.doFilter(null, new MockHttpServletResponse(),
new MockFilterChain());
fail("Should have thrown ServletException");
} catch (ServletException expected) {
assertEquals("HttpServletRequest required", expected.getMessage());
}
}
public void testDoFilterWithNonHttpServletResponseDetected()
throws Exception {
ChannelProcessingFilter filter = new ChannelProcessingFilter();
try {
filter.doFilter(new MockHttpServletRequest(null, null), null,
new MockFilterChain());
fail("Should have thrown ServletException");
} catch (ServletException expected) {
assertEquals("HttpServletResponse required", expected.getMessage());
}
}
public void testDoesNotInterruptRequestsWithCorrectChannelSecurity()
throws Exception {
ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
attr.addConfigAttribute(new SecurityConfig("REQUIRES_SECURE_CHANNEL"));
MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path",
attr);
ChannelProcessingFilter filter = new ChannelProcessingFilter();
filter.setInsecureChannelEntryPoint(new RetryWithHttpEntryPoint());
filter.setSecureChannelEntryPoint(new RetryWithHttpsEntryPoint());
filter.setFilterInvocationDefinitionSource(fids);
filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
MockHttpServletRequest request = new MockHttpServletRequest("info=now");
request.setServletPath("/path");
request.setScheme("https");
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterChain chain = new MockFilterChain(true);
filter.doFilter(request, response, chain);
assertTrue(true);
}
public void testDoesNotInterruptRequestsWithNoConfigAttribute()
throws Exception {
ChannelProcessingFilter filter = new ChannelProcessingFilter();
filter.setInsecureChannelEntryPoint(new RetryWithHttpEntryPoint());
filter.setSecureChannelEntryPoint(new RetryWithHttpsEntryPoint());
filter.setFilterInvocationDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
MockHttpServletRequest request = new MockHttpServletRequest("info=now");
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterChain chain = new MockFilterChain(true);
filter.doFilter(request, response, chain);
assertTrue(true);
}
public void testGetterSetters() {
ChannelProcessingFilter filter = new ChannelProcessingFilter();
filter.setInsecureChannelEntryPoint(new RetryWithHttpEntryPoint());
filter.setSecureChannelEntryPoint(new RetryWithHttpsEntryPoint());
filter.setFilterInvocationDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
assertTrue(filter.getInsecureChannelEntryPoint() != null);
assertTrue(filter.getSecureChannelEntryPoint() != null);
assertTrue(filter.getFilterInvocationDefinitionSource() != null);
assertTrue(filter.getChannelDecisionManager() != null);
}
public void testLifecycle() throws Exception {
ChannelProcessingFilter filter = new ChannelProcessingFilter();
filter.setInsecureChannelEntryPoint(new RetryWithHttpEntryPoint());
filter.setSecureChannelEntryPoint(new RetryWithHttpsEntryPoint());
filter.setFilterInvocationDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
filter.afterPropertiesSet();
filter.init(new MockFilterConfig());
filter.destroy();
}
//~ Inner Classes ==========================================================
private class MockEntryPoint implements ChannelEntryPoint {
private boolean expectToBeCalled;
public MockEntryPoint(boolean expectToBeCalled) {
this.expectToBeCalled = expectToBeCalled;
}
private MockEntryPoint() {
super();
}
public void commence(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if (expectToBeCalled) {
assertTrue(true);
} else {
fail("Did not expect this ChannelEntryPoint to be called");
}
}
}
private class MockFilterChain implements FilterChain {
private boolean expectToProceed;
public MockFilterChain(boolean expectToProceed) {
this.expectToProceed = expectToProceed;
}
private MockFilterChain() {
super();
}
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if (expectToProceed) {
assertTrue(true);
} else {
fail("Did not expect filter chain to proceed");
}
}
}
private class MockFilterInvocationDefinitionMap
implements FilterInvocationDefinitionSource {
private ConfigAttributeDefinition toReturn;
private String servletPath;
public MockFilterInvocationDefinitionMap(String servletPath,
ConfigAttributeDefinition toReturn) {
this.servletPath = servletPath;
this.toReturn = toReturn;
}
private MockFilterInvocationDefinitionMap() {
super();
}
public ConfigAttributeDefinition getAttributes(Object object)
throws IllegalArgumentException {
FilterInvocation fi = (FilterInvocation) object;
if (servletPath.equals(fi.getHttpRequest().getServletPath())) {
return toReturn;
} else {
return null;
}
}
public Iterator getConfigAttributeDefinitions() {
return null;
}
public boolean supports(Class clazz) {
return true;
}
}
}
@@ -0,0 +1,171 @@
/* 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.securechannel;
import junit.framework.TestCase;
import net.sf.acegisecurity.MockHttpServletRequest;
import net.sf.acegisecurity.MockHttpServletResponse;
import net.sf.acegisecurity.MockPortResolver;
import net.sf.acegisecurity.util.PortMapperImpl;
import java.util.HashMap;
import java.util.Map;
/**
* Tests {@link RetryWithHttpEntryPoint}.
*
* @author Ben Alex
* @version $Id$
*/
public class RetryWithHttpEntryPointTests extends TestCase {
//~ Methods ================================================================
public final void setUp() throws Exception {
super.setUp();
}
public static void main(String[] args) {
junit.textui.TestRunner.run(RetryWithHttpEntryPointTests.class);
}
public void testDetectsMissingPortMapper() throws Exception {
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
ep.setPortResolver(new MockPortResolver(80, 443));
try {
ep.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("portMapper is required", expected.getMessage());
}
}
public void testDetectsMissingPortResolver() throws Exception {
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
ep.setPortMapper(new PortMapperImpl());
try {
ep.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("portResolver is required", expected.getMessage());
}
}
public void testGettersSetters() {
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
ep.setPortMapper(new PortMapperImpl());
ep.setPortResolver(new MockPortResolver(8080, 8443));
assertTrue(ep.getPortMapper() != null);
assertTrue(ep.getPortResolver() != null);
}
public void testNormalOperation() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("open=true");
request.setScheme("https");
request.setServerName("www.example.com");
request.setContextPath("/bigWebApp");
request.setServletPath("/hello");
request.setPathInfo("/pathInfo.html");
request.setServerPort(443);
MockHttpServletResponse response = new MockHttpServletResponse();
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
ep.setPortMapper(new PortMapperImpl());
ep.setPortResolver(new MockPortResolver(80, 443));
ep.afterPropertiesSet();
ep.commence(request, response);
System.out.println(response.getRedirect());
assertEquals("http://www.example.com/bigWebApp/hello/pathInfo.html?open=true",
response.getRedirect());
}
public void testNormalOperationWithNullPathInfoAndNullQueryString()
throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest(null);
request.setScheme("https");
request.setServerName("www.example.com");
request.setContextPath("/bigWebApp");
request.setServletPath("/hello");
request.setPathInfo(null);
request.setServerPort(443);
MockHttpServletResponse response = new MockHttpServletResponse();
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
ep.setPortMapper(new PortMapperImpl());
ep.setPortResolver(new MockPortResolver(80, 443));
ep.afterPropertiesSet();
ep.commence(request, response);
System.out.println(response.getRedirect());
assertEquals("http://www.example.com/bigWebApp/hello",
response.getRedirect());
}
public void testOperationWhenTargetPortIsUnknown()
throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("open=true");
request.setScheme("https");
request.setServerName("www.example.com");
request.setContextPath("/bigWebApp");
request.setServletPath("/hello");
request.setPathInfo("/pathInfo.html");
request.setServerPort(8768);
MockHttpServletResponse response = new MockHttpServletResponse();
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
ep.setPortMapper(new PortMapperImpl());
ep.setPortResolver(new MockPortResolver(8768, 1234));
ep.afterPropertiesSet();
ep.commence(request, response);
System.out.println(response.getRedirect());
assertEquals("/bigWebApp", response.getRedirect());
}
public void testOperationWithNonStandardPort() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("open=true");
request.setScheme("https");
request.setServerName("www.example.com");
request.setContextPath("/bigWebApp");
request.setServletPath("/hello");
request.setPathInfo("/pathInfo.html");
request.setServerPort(9999);
MockHttpServletResponse response = new MockHttpServletResponse();
PortMapperImpl portMapper = new PortMapperImpl();
Map map = new HashMap();
map.put("8888", "9999");
portMapper.setPortMappings(map);
RetryWithHttpEntryPoint ep = new RetryWithHttpEntryPoint();
ep.setPortResolver(new MockPortResolver(8888, 9999));
ep.setPortMapper(portMapper);
ep.afterPropertiesSet();
ep.commence(request, response);
System.out.println(response.getRedirect());
assertEquals("http://www.example.com:8888/bigWebApp/hello/pathInfo.html?open=true",
response.getRedirect());
}
}
@@ -0,0 +1,171 @@
/* 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.securechannel;
import junit.framework.TestCase;
import net.sf.acegisecurity.MockHttpServletRequest;
import net.sf.acegisecurity.MockHttpServletResponse;
import net.sf.acegisecurity.MockPortResolver;
import net.sf.acegisecurity.util.PortMapperImpl;
import java.util.HashMap;
import java.util.Map;
/**
* Tests {@link RetryWithHttpsEntryPoint}.
*
* @author Ben Alex
* @version $Id$
*/
public class RetryWithHttpsEntryPointTests extends TestCase {
//~ Methods ================================================================
public final void setUp() throws Exception {
super.setUp();
}
public static void main(String[] args) {
junit.textui.TestRunner.run(RetryWithHttpsEntryPointTests.class);
}
public void testDetectsMissingPortMapper() throws Exception {
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
ep.setPortResolver(new MockPortResolver(80, 443));
try {
ep.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("portMapper is required", expected.getMessage());
}
}
public void testDetectsMissingPortResolver() throws Exception {
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
ep.setPortMapper(new PortMapperImpl());
try {
ep.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("portResolver is required", expected.getMessage());
}
}
public void testGettersSetters() {
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
ep.setPortMapper(new PortMapperImpl());
ep.setPortResolver(new MockPortResolver(8080, 8443));
assertTrue(ep.getPortMapper() != null);
assertTrue(ep.getPortResolver() != null);
}
public void testNormalOperation() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("open=true");
request.setScheme("http");
request.setServerName("www.example.com");
request.setContextPath("/bigWebApp");
request.setServletPath("/hello");
request.setPathInfo("/pathInfo.html");
request.setServerPort(80);
MockHttpServletResponse response = new MockHttpServletResponse();
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
ep.setPortMapper(new PortMapperImpl());
ep.setPortResolver(new MockPortResolver(80, 443));
ep.afterPropertiesSet();
ep.commence(request, response);
System.out.println(response.getRedirect());
assertEquals("https://www.example.com/bigWebApp/hello/pathInfo.html?open=true",
response.getRedirect());
}
public void testNormalOperationWithNullPathInfoAndNullQueryString()
throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest(null);
request.setScheme("http");
request.setServerName("www.example.com");
request.setContextPath("/bigWebApp");
request.setServletPath("/hello");
request.setPathInfo(null);
request.setServerPort(80);
MockHttpServletResponse response = new MockHttpServletResponse();
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
ep.setPortMapper(new PortMapperImpl());
ep.setPortResolver(new MockPortResolver(80, 443));
ep.afterPropertiesSet();
ep.commence(request, response);
System.out.println(response.getRedirect());
assertEquals("https://www.example.com/bigWebApp/hello",
response.getRedirect());
}
public void testOperationWhenTargetPortIsUnknown()
throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("open=true");
request.setScheme("http");
request.setServerName("www.example.com");
request.setContextPath("/bigWebApp");
request.setServletPath("/hello");
request.setPathInfo("/pathInfo.html");
request.setServerPort(8768);
MockHttpServletResponse response = new MockHttpServletResponse();
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
ep.setPortMapper(new PortMapperImpl());
ep.setPortResolver(new MockPortResolver(8768, 1234));
ep.afterPropertiesSet();
ep.commence(request, response);
System.out.println(response.getRedirect());
assertEquals("/bigWebApp", response.getRedirect());
}
public void testOperationWithNonStandardPort() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("open=true");
request.setScheme("http");
request.setServerName("www.example.com");
request.setContextPath("/bigWebApp");
request.setServletPath("/hello");
request.setPathInfo("/pathInfo.html");
request.setServerPort(8888);
MockHttpServletResponse response = new MockHttpServletResponse();
PortMapperImpl portMapper = new PortMapperImpl();
Map map = new HashMap();
map.put("8888", "9999");
portMapper.setPortMappings(map);
RetryWithHttpsEntryPoint ep = new RetryWithHttpsEntryPoint();
ep.setPortResolver(new MockPortResolver(8888, 9999));
ep.setPortMapper(portMapper);
ep.afterPropertiesSet();
ep.commence(request, response);
System.out.println(response.getRedirect());
assertEquals("https://www.example.com:9999/bigWebApp/hello/pathInfo.html?open=true",
response.getRedirect());
}
}