ソースを参照

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

Luke Taylor 17 年 前
コミット
ea70845987

+ 9 - 4
core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java

@@ -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);
                 }

+ 35 - 20
core/src/main/java/org/springframework/security/securechannel/ChannelDecisionManagerImpl.java

@@ -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()) {

+ 1 - 1
core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc

@@ -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.

+ 1 - 0
core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd

@@ -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>

+ 27 - 23
core/src/test/java/org/springframework/security/securechannel/ChannelDecisionManagerImplTests.java

@@ -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) {