3 changed files with 525 additions and 0 deletions
@ -0,0 +1,248 @@ |
|||||||
|
/* 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.util; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
import javax.servlet.Filter; |
||||||
|
import javax.servlet.FilterChain; |
||||||
|
import javax.servlet.FilterConfig; |
||||||
|
import javax.servlet.ServletContext; |
||||||
|
import javax.servlet.ServletException; |
||||||
|
import javax.servlet.ServletRequest; |
||||||
|
import javax.servlet.ServletResponse; |
||||||
|
|
||||||
|
import net.sf.acegisecurity.ConfigAttribute; |
||||||
|
import net.sf.acegisecurity.ConfigAttributeDefinition; |
||||||
|
import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSource; |
||||||
|
|
||||||
|
import org.apache.commons.logging.Log; |
||||||
|
import org.apache.commons.logging.LogFactory; |
||||||
|
import org.springframework.beans.factory.BeanFactoryUtils; |
||||||
|
import org.springframework.beans.factory.InitializingBean; |
||||||
|
import org.springframework.beans.factory.config.BeanPostProcessor; |
||||||
|
import org.springframework.context.ApplicationContext; |
||||||
|
import org.springframework.web.context.support.WebApplicationContextUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* Delegates <code>Filter</code> requests to a Spring-managed bean. |
||||||
|
* <p> |
||||||
|
* This class acts as a proxy on behalf of a target <code>Filter</code> that |
||||||
|
* is defined in the Spring bean context. It is necessary to specify which |
||||||
|
* target <code>Filter</code> should be proxied as a filter initialization |
||||||
|
* parameter. |
||||||
|
* </p> |
||||||
|
* <p> |
||||||
|
* On filter initialisation, the class will use Spring's {@link |
||||||
|
* WebApplicationContextUtils#getWebApplicationContext(ServletContext sc)} |
||||||
|
* method to obtain an <code>ApplicationContext</code> instance. It will |
||||||
|
* expect to find the target <code>Filter</code> in this |
||||||
|
* <code>ApplicationContext</code>. |
||||||
|
* </p> |
||||||
|
* <p> |
||||||
|
* To use this filter, it is necessary to specify <b>one </b> of the following |
||||||
|
* filter initialization parameters: |
||||||
|
* </p> |
||||||
|
* <ul> |
||||||
|
* <li><code>targetClass</code> indicates the class of the target |
||||||
|
* <code>Filter</code> defined in the bean context. The only requirements are |
||||||
|
* that this target class implements the <code>javax.servlet.Filter</code> |
||||||
|
* interface and at least one instance is available in the |
||||||
|
* <code>ApplicationContext</code>.</li> |
||||||
|
* <li><code>targetBean</code> indicates the bean name of the target class. |
||||||
|
* </li> |
||||||
|
* </ul> |
||||||
|
* If both initialization parameters are specified, <code>targetBean</code> |
||||||
|
* takes priority. |
||||||
|
* <P> |
||||||
|
* An additional initialization parameter, <code>init</code>, is also |
||||||
|
* supported. If set to "<code>lazy</code>" the initialization will take |
||||||
|
* place on the first HTTP request, rather than at filter creation time. This |
||||||
|
* makes it possible to use <code>FilterToBeanProxy</code> with the Spring |
||||||
|
* <code>ContextLoaderServlet</code>. Where possible you should not use this |
||||||
|
* initialization parameter, instead using <code>ContextLoaderListener</code>. |
||||||
|
* </p> |
||||||
|
* |
||||||
|
// * <pre>
|
||||||
|
// * <bean id="filterChain" class="net.sf.acegisecurity.FilterChain">
|
||||||
|
// * <property name="filters">
|
||||||
|
// * <value>
|
||||||
|
// * channelProcessingFilter=/*
|
||||||
|
// * authenticationProcessingFilter=/*
|
||||||
|
// * basicProcessingFilter=/*
|
||||||
|
// * sessionIntegrationFilter=/*
|
||||||
|
// * securityEnforcementFilter=/*
|
||||||
|
// * </value>
|
||||||
|
// * </property>
|
||||||
|
// * </bean>
|
||||||
|
// * </pre>
|
||||||
|
* |
||||||
|
* @author Carlos Sanchez |
||||||
|
* @version $Id$ |
||||||
|
*/ |
||||||
|
public class FilterChainProxy |
||||||
|
implements Filter, InitializingBean |
||||||
|
{ |
||||||
|
//~ Static fields/initializers =============================================
|
||||||
|
|
||||||
|
private static final Log logger = LogFactory.getLog(FilterChainProxy.class); |
||||||
|
|
||||||
|
//~ Instance fields
|
||||||
|
// ========================================================
|
||||||
|
|
||||||
|
private Filter delegate; |
||||||
|
|
||||||
|
private List filters; |
||||||
|
|
||||||
|
private FilterConfig filterConfig; |
||||||
|
|
||||||
|
private boolean initialized = false; |
||||||
|
|
||||||
|
private FilterInvocationDefinitionSource filterInvocationDefinitionSource; |
||||||
|
|
||||||
|
//~ Methods
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
|
public void setFilterInvocationDefinitionSource( |
||||||
|
FilterInvocationDefinitionSource filterInvocationDefinitionSource) { |
||||||
|
this.filterInvocationDefinitionSource = filterInvocationDefinitionSource; |
||||||
|
} |
||||||
|
|
||||||
|
public FilterInvocationDefinitionSource getFilterInvocationDefinitionSource() { |
||||||
|
return filterInvocationDefinitionSource; |
||||||
|
} |
||||||
|
|
||||||
|
public void destroy() |
||||||
|
{ |
||||||
|
Iterator it = filters.iterator(); |
||||||
|
while ( it.hasNext() ) |
||||||
|
{ |
||||||
|
Filter filter = (Filter) it.next(); |
||||||
|
if ( filter != null ) |
||||||
|
{ |
||||||
|
filter.destroy(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, |
||||||
|
ServletException |
||||||
|
{ |
||||||
|
if ( !initialized ) |
||||||
|
{ |
||||||
|
doInit(); |
||||||
|
} |
||||||
|
|
||||||
|
Iterator it = filters.iterator(); |
||||||
|
while ( it.hasNext() ) |
||||||
|
{ |
||||||
|
Filter filter = (Filter) it.next(); |
||||||
|
filter.doFilter( request, response, chain ); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void init( FilterConfig filterConfig ) throws ServletException |
||||||
|
{ |
||||||
|
this.filterConfig = filterConfig; |
||||||
|
|
||||||
|
String strategy = filterConfig.getInitParameter( "init" ); |
||||||
|
|
||||||
|
if ( (strategy != null) && strategy.toLowerCase().equals( "lazy" ) ) |
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
doInit(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Allows test cases to override where application context obtained from. |
||||||
|
* |
||||||
|
* @param filterConfig |
||||||
|
* which can be used to find the <code>ServletContext</code> |
||||||
|
* @return the Spring application context |
||||||
|
*/ |
||||||
|
protected ApplicationContext getContext( FilterConfig filterConfig ) |
||||||
|
{ |
||||||
|
return WebApplicationContextUtils.getRequiredWebApplicationContext( filterConfig.getServletContext() ); |
||||||
|
} |
||||||
|
|
||||||
|
private void doInit() throws ServletException |
||||||
|
{ |
||||||
|
initialized = true; |
||||||
|
|
||||||
|
Iterator it = filters.iterator(); |
||||||
|
while ( it.hasNext() ) |
||||||
|
{ |
||||||
|
Filter filter = (Filter) it.next(); |
||||||
|
filter.init( filterConfig ); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void afterPropertiesSet() throws Exception { |
||||||
|
if (filterInvocationDefinitionSource == null) { |
||||||
|
throw new IllegalArgumentException( |
||||||
|
"filterInvocationDefinitionSource must be specified"); |
||||||
|
} |
||||||
|
|
||||||
|
Iterator iter = this.filterInvocationDefinitionSource |
||||||
|
.getConfigAttributeDefinitions(); |
||||||
|
|
||||||
|
if (iter == null) { |
||||||
|
if (logger.isWarnEnabled()) { |
||||||
|
logger.warn( |
||||||
|
"Could not validate configuration attributes as the FilterInvocationDefinitionSource did not return a ConfigAttributeDefinition Iterator"); |
||||||
|
} |
||||||
|
|
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
Set set = new HashSet(); |
||||||
|
|
||||||
|
while (iter.hasNext()) { |
||||||
|
ConfigAttributeDefinition def = (ConfigAttributeDefinition) iter |
||||||
|
.next(); |
||||||
|
Iterator attributes = def.getConfigAttributes(); |
||||||
|
|
||||||
|
while (attributes.hasNext()) { |
||||||
|
ConfigAttribute attr = (ConfigAttribute) attributes.next(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (set.size() == 0) { |
||||||
|
if (logger.isInfoEnabled()) { |
||||||
|
logger.info("Validated configuration attributes"); |
||||||
|
} |
||||||
|
} else { |
||||||
|
throw new IllegalArgumentException( |
||||||
|
"Unsupported configuration attributes: " + set.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
iter = filterInvocationDefinitionSource.getConfigAttributeDefinitions(); |
||||||
|
while ( iter.hasNext() ) |
||||||
|
{ |
||||||
|
ConfigAttributeDefinition element = (ConfigAttributeDefinition) iter.next(); |
||||||
|
Iterator configAttributes = element.getConfigAttributes(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,261 @@ |
|||||||
|
/* 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.util; |
||||||
|
|
||||||
|
import junit.framework.TestCase; |
||||||
|
|
||||||
|
import net.sf.acegisecurity.MockFilterConfig; |
||||||
|
import net.sf.acegisecurity.MockHttpServletRequest; |
||||||
|
import net.sf.acegisecurity.MockHttpServletResponse; |
||||||
|
|
||||||
|
import org.springframework.context.ApplicationContext; |
||||||
|
import org.springframework.context.support.ClassPathXmlApplicationContext; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
import javax.servlet.Filter; |
||||||
|
import javax.servlet.FilterChain; |
||||||
|
import javax.servlet.FilterConfig; |
||||||
|
import javax.servlet.ServletException; |
||||||
|
import javax.servlet.ServletRequest; |
||||||
|
import javax.servlet.ServletResponse; |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests {@link FilterChainProxy}. |
||||||
|
* |
||||||
|
* @author Carlos Sanchez |
||||||
|
* @version $Id$ |
||||||
|
*/ |
||||||
|
public class FilterChainProxyTests |
||||||
|
extends TestCase |
||||||
|
{ |
||||||
|
//~ Constructors
|
||||||
|
// ===========================================================
|
||||||
|
|
||||||
|
public FilterChainProxyTests() |
||||||
|
{ |
||||||
|
super(); |
||||||
|
} |
||||||
|
|
||||||
|
public FilterChainProxyTests( String arg0 ) |
||||||
|
{ |
||||||
|
super( arg0 ); |
||||||
|
} |
||||||
|
|
||||||
|
//~ Methods
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
|
public final void setUp() throws Exception |
||||||
|
{ |
||||||
|
super.setUp(); |
||||||
|
// ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
|
||||||
|
// "net/sf/acegisecurity/util/filtertest-valid.xml" );
|
||||||
|
// FilterChainProxy filterChainProxy = (FilterChainProxy)applicationContext.getBean("filterChain");
|
||||||
|
// System.out.println(filterChainProxy);
|
||||||
|
} |
||||||
|
|
||||||
|
public static void main( String[] args ) |
||||||
|
{ |
||||||
|
junit.textui.TestRunner.run( FilterChainProxyTests.class ); |
||||||
|
} |
||||||
|
|
||||||
|
public void testDetectsTargetBeanIsNotAFilter() throws Exception |
||||||
|
{ |
||||||
|
// Setup our filter
|
||||||
|
MockFilterConfig config = new MockFilterConfig(); |
||||||
|
config.setInitParmeter( "targetClass", "net.sf.acegisecurity.util.MockNotAFilter" ); |
||||||
|
|
||||||
|
FilterToBeanProxy filter = new MockFilterToBeanProxy( "net/sf/acegisecurity/util/filtertest-valid.xml" ); |
||||||
|
|
||||||
|
try |
||||||
|
{ |
||||||
|
filter.init( config ); |
||||||
|
fail( "Should have thrown ServletException" ); |
||||||
|
} |
||||||
|
catch ( ServletException expected ) |
||||||
|
{ |
||||||
|
assertEquals( "Bean 'mockNotAFilter' does not implement javax.servlet.Filter", expected.getMessage() ); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void testDetectsTargetBeanNotInBeanContext() throws Exception |
||||||
|
{ |
||||||
|
// Setup our filter
|
||||||
|
MockFilterConfig config = new MockFilterConfig(); |
||||||
|
config.setInitParmeter( "targetBean", "WRONG_NAME" ); |
||||||
|
|
||||||
|
FilterToBeanProxy filter = new MockFilterToBeanProxy( "net/sf/acegisecurity/util/filtertest-valid.xml" ); |
||||||
|
|
||||||
|
try |
||||||
|
{ |
||||||
|
filter.init( config ); |
||||||
|
fail( "Should have thrown ServletException" ); |
||||||
|
} |
||||||
|
catch ( ServletException expected ) |
||||||
|
{ |
||||||
|
assertEquals( "targetBean 'WRONG_NAME' not found in context", expected.getMessage() ); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void testIgnoresEmptyTargetBean() throws Exception |
||||||
|
{ |
||||||
|
// Setup our filter
|
||||||
|
MockFilterConfig config = new MockFilterConfig(); |
||||||
|
config.setInitParmeter( "targetClass", "net.sf.acegisecurity.util.FilterChainProxy" ); |
||||||
|
config.setInitParmeter( "targetBean", "" ); |
||||||
|
|
||||||
|
// Setup our expectation that the filter chain will be invoked
|
||||||
|
MockFilterChain chain = new MockFilterChain( true ); |
||||||
|
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse(); |
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest( "/go" ); |
||||||
|
|
||||||
|
FilterToBeanProxy filter = new MockFilterToBeanProxy( "net/sf/acegisecurity/util/filtertest-valid.xml" ); |
||||||
|
|
||||||
|
executeFilterInContainerSimulator( config, filter, request, response, chain ); |
||||||
|
} |
||||||
|
|
||||||
|
public void testNormalOperationWithLazyTrue() throws Exception |
||||||
|
{ |
||||||
|
// Setup our filter
|
||||||
|
MockFilterConfig config = new MockFilterConfig(); |
||||||
|
config.setInitParmeter( "targetBean", "filterChain" ); |
||||||
|
config.setInitParmeter( "init", "lazy" ); |
||||||
|
|
||||||
|
// Setup our expectation that the filter chain will be invoked
|
||||||
|
MockFilterChain chain = new MockFilterChain( true ); |
||||||
|
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse(); |
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest( "/go" ); |
||||||
|
|
||||||
|
FilterToBeanProxy filter = new MockFilterToBeanProxy( "net/sf/acegisecurity/util/filtertest-valid.xml" ); |
||||||
|
|
||||||
|
executeFilterInContainerSimulator( config, filter, request, response, chain ); |
||||||
|
} |
||||||
|
|
||||||
|
public void testNormalOperationWithSpecificBeanName() throws Exception |
||||||
|
{ |
||||||
|
// Setup our filter
|
||||||
|
MockFilterConfig config = new MockFilterConfig(); |
||||||
|
config.setInitParmeter( "targetBean", "filterChain" ); |
||||||
|
|
||||||
|
// Setup our expectation that the filter chain will be invoked
|
||||||
|
MockFilterChain chain = new MockFilterChain( true ); |
||||||
|
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse(); |
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest( "/go" ); |
||||||
|
|
||||||
|
FilterToBeanProxy filter = new MockFilterToBeanProxy( "net/sf/acegisecurity/util/filtertest-valid.xml" ); |
||||||
|
|
||||||
|
executeFilterInContainerSimulator( config, filter, request, response, chain ); |
||||||
|
} |
||||||
|
|
||||||
|
public void testNormalOperationWithTargetClass() throws Exception |
||||||
|
{ |
||||||
|
// Setup our filter
|
||||||
|
MockFilterConfig config = new MockFilterConfig(); |
||||||
|
config.setInitParmeter( "targetClass", "net.sf.acegisecurity.util.FilterChainProxy" ); |
||||||
|
|
||||||
|
// Setup our expectation that the filter chain will be invoked
|
||||||
|
MockFilterChain chain = new MockFilterChain( true ); |
||||||
|
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse(); |
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest( "/go" ); |
||||||
|
|
||||||
|
FilterToBeanProxy filter = new MockFilterToBeanProxy( "net/sf/acegisecurity/util/filtertest-valid.xml" ); |
||||||
|
|
||||||
|
executeFilterInContainerSimulator( config, filter, request, response, chain ); |
||||||
|
} |
||||||
|
|
||||||
|
public void testNullDelegateDoesNotCauseNullPointerException() throws Exception |
||||||
|
{ |
||||||
|
// Setup our filter
|
||||||
|
MockFilterConfig config = new MockFilterConfig(); |
||||||
|
config.setInitParmeter( "targetBean", "aFilterThatDoesntExist" ); |
||||||
|
config.setInitParmeter( "init", "lazy" ); |
||||||
|
|
||||||
|
// Setup our expectation that the filter chain will be invoked
|
||||||
|
MockFilterChain chain = new MockFilterChain( true ); |
||||||
|
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse(); |
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest( "/go" ); |
||||||
|
|
||||||
|
FilterToBeanProxy filter = new MockFilterToBeanProxy( "net/sf/acegisecurity/util/filtertest-valid.xml" ); |
||||||
|
|
||||||
|
// do not init (which would hapen if called .doFilter)
|
||||||
|
filter.destroy(); |
||||||
|
} |
||||||
|
|
||||||
|
private void executeFilterInContainerSimulator( FilterConfig filterConfig, Filter filter, ServletRequest request, |
||||||
|
ServletResponse response, FilterChain filterChain ) throws ServletException, IOException |
||||||
|
{ |
||||||
|
filter.init( filterConfig ); |
||||||
|
filter.doFilter( request, response, filterChain ); |
||||||
|
filter.destroy(); |
||||||
|
} |
||||||
|
|
||||||
|
//~ Inner Classes
|
||||||
|
// ==========================================================
|
||||||
|
|
||||||
|
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 MockFilterToBeanProxy |
||||||
|
extends FilterToBeanProxy |
||||||
|
{ |
||||||
|
private String appContextLocation; |
||||||
|
|
||||||
|
public MockFilterToBeanProxy( String appContextLocation ) |
||||||
|
{ |
||||||
|
this.appContextLocation = appContextLocation; |
||||||
|
} |
||||||
|
|
||||||
|
private MockFilterToBeanProxy() |
||||||
|
{ |
||||||
|
super(); |
||||||
|
} |
||||||
|
|
||||||
|
protected ApplicationContext getContext( FilterConfig filterConfig ) |
||||||
|
{ |
||||||
|
return new ClassPathXmlApplicationContext( appContextLocation ); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue