mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-27 14:22:47 +00:00
SEC-335: Support for ANY_CHANNEL configuration attribute in channel processing. Also added to namespace.
This commit is contained in:
parent
2ed1c7d494
commit
ea70845987
@ -60,6 +60,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
|
|
||||||
static final String ATT_ACCESS_CONFIG = "access";
|
static final String ATT_ACCESS_CONFIG = "access";
|
||||||
static final String ATT_REQUIRES_CHANNEL = "requires-channel";
|
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 ATT_CREATE_SESSION = "create-session";
|
||||||
static final String DEF_CREATE_SESSION_IF_REQUIRED = "ifRequired";
|
static final String DEF_CREATE_SESSION_IF_REQUIRED = "ifRequired";
|
||||||
@ -176,7 +179,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
channelFilter.getPropertyValues().addPropertyValue("filterInvocationDefinitionSource",
|
channelFilter.getPropertyValues().addPropertyValue("filterInvocationDefinitionSource",
|
||||||
channelFilterInvDefSource);
|
channelFilterInvDefSource);
|
||||||
RootBeanDefinition channelDecisionManager = new RootBeanDefinition(ChannelDecisionManagerImpl.class);
|
RootBeanDefinition channelDecisionManager = new RootBeanDefinition(ChannelDecisionManagerImpl.class);
|
||||||
ManagedList channelProcessors = new ManagedList(2);
|
ManagedList channelProcessors = new ManagedList(3);
|
||||||
RootBeanDefinition secureChannelProcessor = new RootBeanDefinition(SecureChannelProcessor.class);
|
RootBeanDefinition secureChannelProcessor = new RootBeanDefinition(SecureChannelProcessor.class);
|
||||||
RootBeanDefinition retryWithHttp = new RootBeanDefinition(RetryWithHttpEntryPoint.class);
|
RootBeanDefinition retryWithHttp = new RootBeanDefinition(RetryWithHttpEntryPoint.class);
|
||||||
RootBeanDefinition retryWithHttps = new RootBeanDefinition(RetryWithHttpsEntryPoint.class);
|
RootBeanDefinition retryWithHttps = new RootBeanDefinition(RetryWithHttpsEntryPoint.class);
|
||||||
@ -277,10 +280,12 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||||||
if (StringUtils.hasText(requiredChannel)) {
|
if (StringUtils.hasText(requiredChannel)) {
|
||||||
String channelConfigAttribute = null;
|
String channelConfigAttribute = null;
|
||||||
|
|
||||||
if (requiredChannel.equals("https")) {
|
if (requiredChannel.equals(OPT_REQUIRES_HTTPS)) {
|
||||||
channelConfigAttribute = "REQUIRES_SECURE_CHANNEL";
|
channelConfigAttribute = "REQUIRES_SECURE_CHANNEL";
|
||||||
} else if (requiredChannel.equals("http")) {
|
} else if (requiredChannel.equals(OPT_REQUIRES_HTTP)) {
|
||||||
channelConfigAttribute = "REQUIRES_INSECURE_CHANNEL";
|
channelConfigAttribute = "REQUIRES_INSECURE_CHANNEL";
|
||||||
|
} else if (requiredChannel.equals(OPT_ANY_CHANNEL)) {
|
||||||
|
channelConfigAttribute = ChannelDecisionManagerImpl.ANY_CHANNEL;
|
||||||
} else {
|
} else {
|
||||||
parserContext.getReaderContext().error("Unsupported channel " + requiredChannel, urlElt);
|
parserContext.getReaderContext().error("Unsupported channel " + requiredChannel, urlElt);
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import org.springframework.security.ConfigAttributeDefinition;
|
|||||||
import org.springframework.security.intercept.web.FilterInvocation;
|
import org.springframework.security.intercept.web.FilterInvocation;
|
||||||
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@ -31,16 +32,25 @@ import javax.servlet.ServletException;
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of {@link ChannelDecisionManager}.<p>Iterates through each configured {@link ChannelProcessor}.
|
* Implementation of {@link ChannelDecisionManager}.
|
||||||
* If a <code>ChannelProcessor</code> has any issue with the security of the request, it should cause a redirect,
|
* <p>
|
||||||
* exception or whatever other action is appropriate for the <code>ChannelProcessor</code> implementation.</p>
|
* Iterates through each configured {@link ChannelProcessor}. If a <code>ChannelProcessor</code> has any issue with the
|
||||||
* <P>Once any response is committed (ie a redirect is written to the response object), the
|
* security of the request, it should cause a redirect, exception or whatever other action is appropriate for the
|
||||||
* <code>ChannelDecisionManagerImpl</code> will not iterate through any further <code>ChannelProcessor</code>s.</p>
|
* <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
|
* @author Ben Alex
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public class ChannelDecisionManagerImpl implements ChannelDecisionManager, InitializingBean {
|
public class ChannelDecisionManagerImpl implements ChannelDecisionManager, InitializingBean {
|
||||||
|
|
||||||
|
public static final String ANY_CHANNEL = "ANY_CHANNEL";
|
||||||
|
|
||||||
//~ Instance fields ================================================================================================
|
//~ Instance fields ================================================================================================
|
||||||
|
|
||||||
private List channelProcessors;
|
private List channelProcessors;
|
||||||
@ -52,13 +62,21 @@ public class ChannelDecisionManagerImpl implements ChannelDecisionManager, Initi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkIfValidList(List listToCheck) {
|
private void checkIfValidList(List listToCheck) {
|
||||||
if ((listToCheck == null) || (listToCheck.size() == 0)) {
|
Assert.notEmpty(listToCheck, "A list of ChannelProcessors is required");
|
||||||
throw new IllegalArgumentException("A list of ChannelProcessors is required");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void decide(FilterInvocation invocation, ConfigAttributeDefinition config)
|
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();
|
Iterator iter = this.channelProcessors.iterator();
|
||||||
|
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
@ -72,7 +90,7 @@ public class ChannelDecisionManagerImpl implements ChannelDecisionManager, Initi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List getChannelProcessors() {
|
protected List getChannelProcessors() {
|
||||||
return this.channelProcessors;
|
return this.channelProcessors;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,22 +100,19 @@ public class ChannelDecisionManagerImpl implements ChannelDecisionManager, Initi
|
|||||||
Iterator iter = newList.iterator();
|
Iterator iter = newList.iterator();
|
||||||
|
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
Object currentObject = null;
|
Object currentObject = iter.next();
|
||||||
|
Assert.isInstanceOf(ChannelProcessor.class, currentObject, "ChannelProcessor " +
|
||||||
try {
|
currentObject.getClass().getName() + " must implement ChannelProcessor");
|
||||||
currentObject = iter.next();
|
|
||||||
|
|
||||||
ChannelProcessor attemptToCast = (ChannelProcessor) currentObject;
|
|
||||||
} catch (ClassCastException cce) {
|
|
||||||
throw new IllegalArgumentException("ChannelProcessor " + currentObject.getClass().getName()
|
|
||||||
+ " must implement ChannelProcessor");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.channelProcessors = newList;
|
this.channelProcessors = newList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean supports(ConfigAttribute attribute) {
|
public boolean supports(ConfigAttribute attribute) {
|
||||||
|
if (ANY_CHANNEL.equals(attribute.getAttribute())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Iterator iter = this.channelProcessors.iterator();
|
Iterator iter = this.channelProcessors.iterator();
|
||||||
|
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
|
@ -135,7 +135,7 @@ intercept-url.attlist &=
|
|||||||
attribute filters {"none"}?
|
attribute filters {"none"}?
|
||||||
intercept-url.attlist &=
|
intercept-url.attlist &=
|
||||||
## Used to specify that a URL must be accessed over http or https
|
## 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 =
|
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.
|
## 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.
|
||||||
|
@ -368,6 +368,7 @@
|
|||||||
<xs:restriction base="xs:token">
|
<xs:restriction base="xs:token">
|
||||||
<xs:enumeration value="http"/>
|
<xs:enumeration value="http"/>
|
||||||
<xs:enumeration value="https"/>
|
<xs:enumeration value="https"/>
|
||||||
|
<xs:enumeration value="any"/>
|
||||||
</xs:restriction>
|
</xs:restriction>
|
||||||
</xs:simpleType>
|
</xs:simpleType>
|
||||||
</xs:attribute>
|
</xs:attribute>
|
||||||
|
@ -45,16 +45,7 @@ import javax.servlet.ServletException;
|
|||||||
public class ChannelDecisionManagerImplTests extends TestCase {
|
public class ChannelDecisionManagerImplTests extends TestCase {
|
||||||
//~ Methods ========================================================================================================
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public void testCannotSetEmptyChannelProcessorsList() throws Exception {
|
||||||
junit.textui.TestRunner.run(ChannelDecisionManagerImplTests.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setUp() throws Exception {
|
|
||||||
super.setUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCannotSetEmptyChannelProcessorsList()
|
|
||||||
throws Exception {
|
|
||||||
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -65,8 +56,7 @@ public class ChannelDecisionManagerImplTests extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCannotSetIncorrectObjectTypesIntoChannelProcessorsList()
|
public void testCannotSetIncorrectObjectTypesIntoChannelProcessorsList() throws Exception {
|
||||||
throws Exception {
|
|
||||||
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||||
List list = new Vector();
|
List list = new Vector();
|
||||||
list.add("THIS IS NOT A CHANNELPROCESSOR");
|
list.add("THIS IS NOT A CHANNELPROCESSOR");
|
||||||
@ -79,8 +69,7 @@ public class ChannelDecisionManagerImplTests extends TestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCannotSetNullChannelProcessorsList()
|
public void testCannotSetNullChannelProcessorsList() throws Exception {
|
||||||
throws Exception {
|
|
||||||
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -113,8 +102,28 @@ public class ChannelDecisionManagerImplTests extends TestCase {
|
|||||||
assertTrue(fi.getResponse().isCommitted());
|
assertTrue(fi.getResponse().isCommitted());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDecideIteratesAllProcessorsIfNoneCommitAResponse()
|
public void testAnyChannelAttributeCausesProcessorsToBeSkipped() throws Exception {
|
||||||
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();
|
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||||
MockChannelProcessor cpXyz = new MockChannelProcessor("xyz", false);
|
MockChannelProcessor cpXyz = new MockChannelProcessor("xyz", false);
|
||||||
MockChannelProcessor cpAbc = new MockChannelProcessor("abc", false);
|
MockChannelProcessor cpAbc = new MockChannelProcessor("abc", false);
|
||||||
@ -165,8 +174,7 @@ public class ChannelDecisionManagerImplTests extends TestCase {
|
|||||||
assertEquals(list, cdm.getChannelProcessors());
|
assertEquals(list, cdm.getChannelProcessors());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testStartupFailsWithEmptyChannelProcessorsList()
|
public void testStartupFailsWithEmptyChannelProcessorsList() throws Exception {
|
||||||
throws Exception {
|
|
||||||
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -188,12 +196,8 @@ public class ChannelDecisionManagerImplTests extends TestCase {
|
|||||||
this.failIfCalled = failIfCalled;
|
this.failIfCalled = failIfCalled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MockChannelProcessor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void decide(FilterInvocation invocation, ConfigAttributeDefinition config)
|
public void decide(FilterInvocation invocation, ConfigAttributeDefinition config)
|
||||||
throws IOException, ServletException {
|
throws IOException, ServletException {
|
||||||
Iterator iter = config.getConfigAttributes();
|
Iterator iter = config.getConfigAttributes();
|
||||||
|
|
||||||
if (failIfCalled) {
|
if (failIfCalled) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user