SEC-335: Support for ANY_CHANNEL configuration attribute in channel processing. Also added to namespace.

This commit is contained in:
Luke Taylor 2008-01-17 20:52:26 +00:00
parent 2ed1c7d494
commit ea70845987
5 changed files with 73 additions and 48 deletions

View File

@ -60,6 +60,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
static final String ATT_ACCESS_CONFIG = "access";
static final String ATT_REQUIRES_CHANNEL = "requires-channel";
static final String OPT_REQUIRES_HTTP = "http";
static final String OPT_REQUIRES_HTTPS = "https";
static final String OPT_ANY_CHANNEL = "any";
static final String ATT_CREATE_SESSION = "create-session";
static final String DEF_CREATE_SESSION_IF_REQUIRED = "ifRequired";
@ -176,12 +179,12 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
channelFilter.getPropertyValues().addPropertyValue("filterInvocationDefinitionSource",
channelFilterInvDefSource);
RootBeanDefinition channelDecisionManager = new RootBeanDefinition(ChannelDecisionManagerImpl.class);
ManagedList channelProcessors = new ManagedList(2);
ManagedList channelProcessors = new ManagedList(3);
RootBeanDefinition secureChannelProcessor = new RootBeanDefinition(SecureChannelProcessor.class);
RootBeanDefinition retryWithHttp = new RootBeanDefinition(RetryWithHttpEntryPoint.class);
RootBeanDefinition retryWithHttps = new RootBeanDefinition(RetryWithHttpsEntryPoint.class);
retryWithHttp.getPropertyValues().addPropertyValue("portMapper", portMapperRef);
retryWithHttps.getPropertyValues().addPropertyValue("portMapper", portMapperRef);
retryWithHttps.getPropertyValues().addPropertyValue("portMapper", portMapperRef);
secureChannelProcessor.getPropertyValues().addPropertyValue("entryPoint", retryWithHttps);
RootBeanDefinition inSecureChannelProcessor = new RootBeanDefinition(InsecureChannelProcessor.class);
inSecureChannelProcessor.getPropertyValues().addPropertyValue("entryPoint", retryWithHttp);
@ -277,10 +280,12 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
if (StringUtils.hasText(requiredChannel)) {
String channelConfigAttribute = null;
if (requiredChannel.equals("https")) {
if (requiredChannel.equals(OPT_REQUIRES_HTTPS)) {
channelConfigAttribute = "REQUIRES_SECURE_CHANNEL";
} else if (requiredChannel.equals("http")) {
} else if (requiredChannel.equals(OPT_REQUIRES_HTTP)) {
channelConfigAttribute = "REQUIRES_INSECURE_CHANNEL";
} else if (requiredChannel.equals(OPT_ANY_CHANNEL)) {
channelConfigAttribute = ChannelDecisionManagerImpl.ANY_CHANNEL;
} else {
parserContext.getReaderContext().error("Unsupported channel " + requiredChannel, urlElt);
}

View File

@ -21,6 +21,7 @@ import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.intercept.web.FilterInvocation;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import java.io.IOException;
@ -31,16 +32,25 @@ import javax.servlet.ServletException;
/**
* Implementation of {@link ChannelDecisionManager}.<p>Iterates through each configured {@link ChannelProcessor}.
* If a <code>ChannelProcessor</code> has any issue with the security of the request, it should cause a redirect,
* exception or whatever other action is appropriate for the <code>ChannelProcessor</code> implementation.</p>
* <P>Once any response is committed (ie a redirect is written to the response object), the
* <code>ChannelDecisionManagerImpl</code> will not iterate through any further <code>ChannelProcessor</code>s.</p>
* Implementation of {@link ChannelDecisionManager}.
* <p>
* Iterates through each configured {@link ChannelProcessor}. If a <code>ChannelProcessor</code> has any issue with the
* security of the request, it should cause a redirect, exception or whatever other action is appropriate for the
* <code>ChannelProcessor</code> implementation.
* <p>
* Once any response is committed (ie a redirect is written to the response object), the
* <code>ChannelDecisionManagerImpl</code> will not iterate through any further <code>ChannelProcessor</code>s.
* <p>
* The attribute "ANY_CHANNEL" if applied to a particular URL, the iteration through the channel processors will be
* skipped (see SEC-494, SEC-335).
*
* @author Ben Alex
* @version $Id$
*/
public class ChannelDecisionManagerImpl implements ChannelDecisionManager, InitializingBean {
public static final String ANY_CHANNEL = "ANY_CHANNEL";
//~ Instance fields ================================================================================================
private List channelProcessors;
@ -52,13 +62,21 @@ public class ChannelDecisionManagerImpl implements ChannelDecisionManager, Initi
}
private void checkIfValidList(List listToCheck) {
if ((listToCheck == null) || (listToCheck.size() == 0)) {
throw new IllegalArgumentException("A list of ChannelProcessors is required");
}
Assert.notEmpty(listToCheck, "A list of ChannelProcessors is required");
}
public void decide(FilterInvocation invocation, ConfigAttributeDefinition config)
throws IOException, ServletException {
throws IOException, ServletException {
Iterator attrs = config.getConfigAttributes();
while (attrs.hasNext()) {
ConfigAttribute attribute = (ConfigAttribute) attrs.next();
if (ANY_CHANNEL.equals(attribute.getAttribute())) {
return;
}
}
Iterator iter = this.channelProcessors.iterator();
while (iter.hasNext()) {
@ -72,7 +90,7 @@ public class ChannelDecisionManagerImpl implements ChannelDecisionManager, Initi
}
}
public List getChannelProcessors() {
protected List getChannelProcessors() {
return this.channelProcessors;
}
@ -82,22 +100,19 @@ public class ChannelDecisionManagerImpl implements ChannelDecisionManager, Initi
Iterator iter = newList.iterator();
while (iter.hasNext()) {
Object currentObject = null;
try {
currentObject = iter.next();
ChannelProcessor attemptToCast = (ChannelProcessor) currentObject;
} catch (ClassCastException cce) {
throw new IllegalArgumentException("ChannelProcessor " + currentObject.getClass().getName()
+ " must implement ChannelProcessor");
}
Object currentObject = iter.next();
Assert.isInstanceOf(ChannelProcessor.class, currentObject, "ChannelProcessor " +
currentObject.getClass().getName() + " must implement ChannelProcessor");
}
this.channelProcessors = newList;
}
public boolean supports(ConfigAttribute attribute) {
if (ANY_CHANNEL.equals(attribute.getAttribute())) {
return true;
}
Iterator iter = this.channelProcessors.iterator();
while (iter.hasNext()) {

View File

@ -135,7 +135,7 @@ intercept-url.attlist &=
attribute filters {"none"}?
intercept-url.attlist &=
## Used to specify that a URL must be accessed over http or https
attribute requires-channel {"http" | "https"}?
attribute requires-channel {"http" | "https" | "any"}?
logout =
## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.

View File

@ -368,6 +368,7 @@
<xs:restriction base="xs:token">
<xs:enumeration value="http"/>
<xs:enumeration value="https"/>
<xs:enumeration value="any"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>

View File

@ -45,16 +45,7 @@ import javax.servlet.ServletException;
public class ChannelDecisionManagerImplTests extends TestCase {
//~ Methods ========================================================================================================
public static void main(String[] args) {
junit.textui.TestRunner.run(ChannelDecisionManagerImplTests.class);
}
public final void setUp() throws Exception {
super.setUp();
}
public void testCannotSetEmptyChannelProcessorsList()
throws Exception {
public void testCannotSetEmptyChannelProcessorsList() throws Exception {
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
try {
@ -65,8 +56,7 @@ public class ChannelDecisionManagerImplTests extends TestCase {
}
}
public void testCannotSetIncorrectObjectTypesIntoChannelProcessorsList()
throws Exception {
public void testCannotSetIncorrectObjectTypesIntoChannelProcessorsList() throws Exception {
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
List list = new Vector();
list.add("THIS IS NOT A CHANNELPROCESSOR");
@ -79,8 +69,7 @@ public class ChannelDecisionManagerImplTests extends TestCase {
}
}
public void testCannotSetNullChannelProcessorsList()
throws Exception {
public void testCannotSetNullChannelProcessorsList() throws Exception {
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
try {
@ -113,8 +102,28 @@ public class ChannelDecisionManagerImplTests extends TestCase {
assertTrue(fi.getResponse().isCommitted());
}
public void testDecideIteratesAllProcessorsIfNoneCommitAResponse()
throws Exception {
public void testAnyChannelAttributeCausesProcessorsToBeSkipped() throws Exception {
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
MockChannelProcessor cpAbc = new MockChannelProcessor("abc", true);
List list = new Vector();
list.add(cpAbc);
cdm.setChannelProcessors(list);
cdm.afterPropertiesSet();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterChain chain = new MockFilterChain();
FilterInvocation fi = new FilterInvocation(request, response, chain);
ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
cad.addConfigAttribute(new SecurityConfig("abc"));
cad.addConfigAttribute(new SecurityConfig("ANY_CHANNEL"));
cdm.decide(fi, cad);
assertFalse(fi.getResponse().isCommitted());
}
public void testDecideIteratesAllProcessorsIfNoneCommitAResponse() throws Exception {
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
MockChannelProcessor cpXyz = new MockChannelProcessor("xyz", false);
MockChannelProcessor cpAbc = new MockChannelProcessor("abc", false);
@ -165,8 +174,7 @@ public class ChannelDecisionManagerImplTests extends TestCase {
assertEquals(list, cdm.getChannelProcessors());
}
public void testStartupFailsWithEmptyChannelProcessorsList()
throws Exception {
public void testStartupFailsWithEmptyChannelProcessorsList() throws Exception {
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
try {
@ -188,12 +196,8 @@ public class ChannelDecisionManagerImplTests extends TestCase {
this.failIfCalled = failIfCalled;
}
private MockChannelProcessor() {
super();
}
public void decide(FilterInvocation invocation, ConfigAttributeDefinition config)
throws IOException, ServletException {
throws IOException, ServletException {
Iterator iter = config.getConfigAttributes();
if (failIfCalled) {