Sfoglia il codice sorgente

Make ChannelDecisionManagerImpl iterate through a list of channel security processors.

Ben Alex 21 anni fa
parent
commit
ecac5a2eed
19 ha cambiato i file con 1122 aggiunte e 536 eliminazioni
  1. 2 2
      changelog.txt
  2. 30 2
      core/src/main/java/org/acegisecurity/securechannel/ChannelDecisionManager.java
  3. 63 59
      core/src/main/java/org/acegisecurity/securechannel/ChannelDecisionManagerImpl.java
  4. 7 8
      core/src/main/java/org/acegisecurity/securechannel/ChannelEntryPoint.java
  5. 45 44
      core/src/main/java/org/acegisecurity/securechannel/ChannelProcessingFilter.java
  6. 72 0
      core/src/main/java/org/acegisecurity/securechannel/ChannelProcessor.java
  7. 117 0
      core/src/main/java/org/acegisecurity/securechannel/InsecureChannelProcessor.java
  8. 0 50
      core/src/main/java/org/acegisecurity/securechannel/InsecureChannelRequiredException.java
  9. 116 0
      core/src/main/java/org/acegisecurity/securechannel/SecureChannelProcessor.java
  10. 0 50
      core/src/main/java/org/acegisecurity/securechannel/SecureChannelRequiredException.java
  11. 5 1
      core/src/test/java/org/acegisecurity/MockHttpServletResponse.java
  12. 129 86
      core/src/test/java/org/acegisecurity/securechannel/ChannelDecisionManagerImplTests.java
  13. 139 131
      core/src/test/java/org/acegisecurity/securechannel/ChannelProcessingFilterTests.java
  14. 153 0
      core/src/test/java/org/acegisecurity/securechannel/InsecureChannelProcessorTests.java
  15. 153 0
      core/src/test/java/org/acegisecurity/securechannel/SecureChannelProcessorTests.java
  16. 71 59
      docs/reference/src/index.xml
  17. 9 20
      samples/contacts/etc/cas/applicationContext.xml
  18. 9 22
      samples/contacts/etc/filter/applicationContext.xml
  19. 2 2
      samples/contacts/etc/filter/web.xml

+ 2 - 2
changelog.txt

@@ -1,4 +1,4 @@
-Changes in version 0.5 (2004-04-28)
+Changes in version 0.5 (2004-04-29)
 -----------------------------------
 -----------------------------------
 
 
 * Added single sign on support via Yale Central Authentication Service (CAS)
 * Added single sign on support via Yale Central Authentication Service (CAS)
@@ -13,7 +13,7 @@ Changes in version 0.5 (2004-04-28)
 * Added definable prefixes to avoid expectation of "ROLE_" GrantedAuthoritys
 * Added definable prefixes to avoid expectation of "ROLE_" GrantedAuthoritys
 * Added pluggable AuthenticationEntryPoints to SecurityEnforcementFilter
 * Added pluggable AuthenticationEntryPoints to SecurityEnforcementFilter
 * Added Apache Ant path syntax support to SecurityEnforcementFilter
 * Added Apache Ant path syntax support to SecurityEnforcementFilter
-* Added filter to automate entry into secure channels, such as HTTPS
+* Added filter to automate web channel requirements (eg HTTPS redirection)
 * Updated JAR to Spring 1.0.1
 * Updated JAR to Spring 1.0.1
 * Updated several classes to use absolute (not relative) redirection URLs
 * Updated several classes to use absolute (not relative) redirection URLs
 * Refactored filters to use Spring application context lifecycle support
 * Refactored filters to use Spring application context lifecycle support

+ 30 - 2
core/src/main/java/org/acegisecurity/securechannel/ChannelDecisionManager.java

@@ -15,12 +15,23 @@
 
 
 package net.sf.acegisecurity.securechannel;
 package net.sf.acegisecurity.securechannel;
 
 
+import net.sf.acegisecurity.ConfigAttribute;
 import net.sf.acegisecurity.ConfigAttributeDefinition;
 import net.sf.acegisecurity.ConfigAttributeDefinition;
 import net.sf.acegisecurity.intercept.web.FilterInvocation;
 import net.sf.acegisecurity.intercept.web.FilterInvocation;
 
 
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
 
 
 /**
 /**
  * Decides whether a web channel provides sufficient security.
  * Decides whether a web channel provides sufficient security.
+ * 
+ * <P>
+ * If necessary due to the nature of the redirection, implementations should
+ * store the original destination of the request in {@link
+ * net.sf.acegisecurity.ui.AbstractProcessingFilter#ACEGI_SECURITY_TARGET_URL_KEY}.
+ * </p>
  *
  *
  * @author Ben Alex
  * @author Ben Alex
  * @version $Id$
  * @version $Id$
@@ -34,6 +45,23 @@ public interface ChannelDecisionManager {
      * ConfigAttributeDefinition}.
      * ConfigAttributeDefinition}.
      */
      */
     public void decide(FilterInvocation invocation,
     public void decide(FilterInvocation invocation,
-        ConfigAttributeDefinition config)
-        throws InsecureChannelRequiredException, SecureChannelRequiredException;
+        ConfigAttributeDefinition config) throws IOException, ServletException;
+
+    /**
+     * Indicates whether this <code>ChannelDecisionManager</code> is able to
+     * process the passed <code>ConfigAttribute</code>.
+     * 
+     * <p>
+     * This allows the <code>ChannelProcessingFilter</code> to check every
+     * configuration attribute can be consumed by the configured
+     * <code>ChannelDecisionManager</code>.
+     * </p>
+     *
+     * @param attribute a configuration attribute that has been configured
+     *        against the <code>ChannelProcessingFilter</code>
+     *
+     * @return true if this <code>ChannelDecisionManager</code> can support the
+     *         passed configuration attribute
+     */
+    public boolean supports(ConfigAttribute attribute);
 }
 }

+ 63 - 59
core/src/main/java/org/acegisecurity/securechannel/ChannelDecisionManagerImpl.java

@@ -21,100 +21,104 @@ import net.sf.acegisecurity.intercept.web.FilterInvocation;
 
 
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.InitializingBean;
 
 
+import java.io.IOException;
+
 import java.util.Iterator;
 import java.util.Iterator;
+import java.util.List;
+
+import javax.servlet.ServletException;
 
 
 
 
 /**
 /**
- * <p>
- * Ensures configuration attribute requested channel security is present by
- * review of <code>HttpServletRequest.isSecure()</code> responses.
- * </p>
+ * Implementation of {@link ChannelDecisionManager}.
  * 
  * 
- * <P>
- * 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>
+ * 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>
  * 
  * 
  * <P>
  * <P>
- * The default <code>secureKeyword</code> and <code>insecureKeyword</code> is
- * <code>REQUIRES_SECURE_CHANNEL</code> and
- * <code>REQUIRES_INSECURE_CHANNEL</code> respectively.
+ * 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>
  * </p>
  *
  *
  * @author Ben Alex
  * @author Ben Alex
  * @version $Id$
  * @version $Id$
  */
  */
-public class ChannelDecisionManagerImpl implements InitializingBean,
-    ChannelDecisionManager {
+public class ChannelDecisionManagerImpl implements ChannelDecisionManager,
+    InitializingBean {
     //~ Instance fields ========================================================
     //~ Instance fields ========================================================
 
 
-    private String insecureKeyword = "REQUIRES_INSECURE_CHANNEL";
-    private String secureKeyword = "REQUIRES_SECURE_CHANNEL";
+    private List channelProcessors;
 
 
     //~ Methods ================================================================
     //~ Methods ================================================================
 
 
-    public void setInsecureKeyword(String insecureKeyword) {
-        this.insecureKeyword = insecureKeyword;
-    }
+    public void setChannelProcessors(List newList) {
+        checkIfValidList(newList);
 
 
-    public String getInsecureKeyword() {
-        return insecureKeyword;
-    }
+        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");
+            }
+        }
 
 
-    public void setSecureKeyword(String secureKeyword) {
-        this.secureKeyword = secureKeyword;
+        this.channelProcessors = newList;
     }
     }
 
 
-    public String getSecureKeyword() {
-        return secureKeyword;
+    public List getChannelProcessors() {
+        return this.channelProcessors;
     }
     }
 
 
     public void afterPropertiesSet() throws Exception {
     public void afterPropertiesSet() throws Exception {
-        if ((secureKeyword == null) || "".equals(secureKeyword)) {
-            throw new IllegalArgumentException("secureKeyword required");
-        }
-
-        if ((insecureKeyword == null) || "".equals(insecureKeyword)) {
-            throw new IllegalArgumentException("insecureKeyword required");
-        }
+        checkIfValidList(this.channelProcessors);
     }
     }
 
 
     public void decide(FilterInvocation invocation,
     public void decide(FilterInvocation invocation,
-        ConfigAttributeDefinition config) throws SecureChannelRequiredException {
-        if ((invocation == null) || (config == null)) {
-            throw new IllegalArgumentException("Nulls cannot be provided");
+        ConfigAttributeDefinition config) throws IOException, ServletException {
+        Iterator iter = this.channelProcessors.iterator();
+
+        while (iter.hasNext()) {
+            ChannelProcessor processor = (ChannelProcessor) iter.next();
+
+            processor.decide(invocation, config);
+
+            if (invocation.getResponse().isCommitted()) {
+                break;
+            }
         }
         }
+    }
 
 
-        Iterator iter = config.getConfigAttributes();
+    public boolean supports(ConfigAttribute attribute) {
+        Iterator iter = this.channelProcessors.iterator();
 
 
         while (iter.hasNext()) {
         while (iter.hasNext()) {
-            ConfigAttribute attribute = (ConfigAttribute) iter.next();
+            ChannelProcessor processor = (ChannelProcessor) iter.next();
 
 
-            if (attribute.equals(secureKeyword)) {
-                if (!invocation.getHttpRequest().isSecure()) {
-                    throw new SecureChannelRequiredException(
-                        "Request is not being made over a secure channel");
-                }
+            if (processor.supports(attribute)) {
+                return true;
             }
             }
+        }
 
 
-            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");
-                }
-            }
+        return false;
+    }
+
+    private void checkIfValidList(List listToCheck) {
+        if ((listToCheck == null) || (listToCheck.size() == 0)) {
+            throw new IllegalArgumentException(
+                "A list of ChannelProcessors is required");
         }
         }
     }
     }
 }
 }

+ 7 - 8
core/src/main/java/org/acegisecurity/securechannel/ChannelEntryPoint.java

@@ -23,11 +23,13 @@ import javax.servlet.ServletResponse;
 
 
 
 
 /**
 /**
- * Used by {@link ChannelProcessingFilter} to launch a web channel.
+ * May be used by a {@link ChannelProcessor} to launch a web channel.
  * 
  * 
  * <P>
  * <P>
- * Depending on the implementation, a secure or insecure channel will be
- * launched.
+ * <code>ChannelProcessor</code>s can elect to launch a new web channel
+ * directly, or they can delegate to another class. The
+ * <code>ChannelEntryPoint</code> is a pluggable interface to assist
+ * <code>ChannelProcessor</code>s in performing this delegation.
  * </p>
  * </p>
  *
  *
  * @author Ben Alex
  * @author Ben Alex
@@ -42,13 +44,10 @@ public interface ChannelEntryPoint {
      * <P>
      * <P>
      * Implementations should modify the headers on the
      * Implementations should modify the headers on the
      * <code>ServletResponse</code> as necessary to commence the user agent
      * <code>ServletResponse</code> as necessary to commence the user agent
-     * using the implementation's supported channel type (ie secure or
-     * insecure).
+     * using the implementation's supported channel type.
      * </p>
      * </p>
      *
      *
-     * @param request that resulted in a
-     *        <code>SecureChannelRequiredException</code> or
-     *        <code>InsecureChannelRequiredException</code>
+     * @param request that a <code>ChannelProcessor</code> has rejected
      * @param response so that the user agent can begin using a new channel
      * @param response so that the user agent can begin using a new channel
      */
      */
     public void commence(ServletRequest request, ServletResponse response)
     public void commence(ServletRequest request, ServletResponse response)

+ 45 - 44
core/src/main/java/org/acegisecurity/securechannel/ChannelProcessingFilter.java

@@ -15,6 +15,7 @@
 
 
 package net.sf.acegisecurity.securechannel;
 package net.sf.acegisecurity.securechannel;
 
 
+import net.sf.acegisecurity.ConfigAttribute;
 import net.sf.acegisecurity.ConfigAttributeDefinition;
 import net.sf.acegisecurity.ConfigAttributeDefinition;
 import net.sf.acegisecurity.intercept.web.FilterInvocation;
 import net.sf.acegisecurity.intercept.web.FilterInvocation;
 import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSource;
 import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSource;
@@ -26,6 +27,10 @@ import org.springframework.beans.factory.InitializingBean;
 
 
 import java.io.IOException;
 import java.io.IOException;
 
 
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
 import javax.servlet.Filter;
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
 import javax.servlet.FilterConfig;
@@ -46,6 +51,12 @@ import javax.servlet.http.HttpServletResponse;
  * </p>
  * </p>
  * 
  * 
  * <P>
  * <P>
+ * Delegates the actual channel security decisions and necessary actions to the
+ * configured {@link ChannelDecisionManager}. If a response is committed by
+ * the <code>ChannelDecisionManager</code>, the filter chain will not proceed.
+ * </p>
+ * 
+ * <P>
  * <B>Do not use this class directly.</B> Instead configure
  * <B>Do not use this class directly.</B> Instead configure
  * <code>web.xml</code> to use the {@link
  * <code>web.xml</code> to use the {@link
  * net.sf.acegisecurity.util.FilterToBeanProxy}.
  * net.sf.acegisecurity.util.FilterToBeanProxy}.
@@ -62,8 +73,6 @@ public class ChannelProcessingFilter implements InitializingBean, Filter {
     //~ Instance fields ========================================================
     //~ Instance fields ========================================================
 
 
     private ChannelDecisionManager channelDecisionManager;
     private ChannelDecisionManager channelDecisionManager;
-    private ChannelEntryPoint insecureChannelEntryPoint;
-    private ChannelEntryPoint secureChannelEntryPoint;
     private FilterInvocationDefinitionSource filterInvocationDefinitionSource;
     private FilterInvocationDefinitionSource filterInvocationDefinitionSource;
 
 
     //~ Methods ================================================================
     //~ Methods ================================================================
@@ -86,23 +95,6 @@ public class ChannelProcessingFilter implements InitializingBean, Filter {
         return filterInvocationDefinitionSource;
         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 {
     public void afterPropertiesSet() throws Exception {
         if (filterInvocationDefinitionSource == null) {
         if (filterInvocationDefinitionSource == null) {
             throw new IllegalArgumentException(
             throw new IllegalArgumentException(
@@ -114,14 +106,41 @@ public class ChannelProcessingFilter implements InitializingBean, Filter {
                 "channelDecisionManager must be specified");
                 "channelDecisionManager must be specified");
         }
         }
 
 
-        if (secureChannelEntryPoint == null) {
-            throw new IllegalArgumentException(
-                "secureChannelEntryPoint 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 (!this.channelDecisionManager.supports(attr)) {
+                    set.add(attr);
+                }
+            }
         }
         }
 
 
-        if (insecureChannelEntryPoint == null) {
+        if (set.size() == 0) {
+            if (logger.isInfoEnabled()) {
+                logger.info("Validated configuration attributes");
+            }
+        } else {
             throw new IllegalArgumentException(
             throw new IllegalArgumentException(
-                "insecureChannelEntryPoint must be specified");
+                "Unsupported configuration attributes: " + set.toString());
         }
         }
     }
     }
 
 
@@ -147,27 +166,9 @@ public class ChannelProcessingFilter implements InitializingBean, Filter {
                     + "; ConfigAttributes: " + attr.toString());
                     + "; ConfigAttributes: " + attr.toString());
             }
             }
 
 
-            try {
-                channelDecisionManager.decide(fi, attr);
-            } catch (SecureChannelRequiredException secureException) {
-                if (logger.isDebugEnabled()) {
-                    logger.debug("Channel insufficient security ("
-                        + secureException.getMessage()
-                        + "); delegating to secureChannelEntryPoint");
-                }
-
-                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);
+            channelDecisionManager.decide(fi, attr);
 
 
+            if (fi.getResponse().isCommitted()) {
                 return;
                 return;
             }
             }
         }
         }

+ 72 - 0
core/src/main/java/org/acegisecurity/securechannel/ChannelProcessor.java

@@ -0,0 +1,72 @@
+/* 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.ConfigAttribute;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.intercept.web.FilterInvocation;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+
+/**
+ * Decides whether a web channel meets a specific security condition.
+ * 
+ * <P>
+ * <code>ChannelProcessor</code> implementations are iterated by the {@link
+ * ChannelDecisionManagerImpl}.
+ * </p>
+ * 
+ * <P>
+ * If an implementation has an issue with the channel security, they should
+ * take action themselves. The callers of the implementation do not take any
+ * action.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public interface ChannelProcessor {
+    //~ Methods ================================================================
+
+    /**
+     * 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 IOException, ServletException;
+
+    /**
+     * Indicates whether this <code>ChannelProcessor</code> is able to process
+     * the passed <code>ConfigAttribute</code>.
+     * 
+     * <p>
+     * This allows the <code>ChannelProcessingFilter</code> to check every
+     * configuration attribute can be consumed by the configured
+     * <code>ChannelDecisionManager</code>.
+     * </p>
+     *
+     * @param attribute a configuration attribute that has been configured
+     *        against the <code>ChannelProcessingFilter</code>
+     *
+     * @return true if this <code>ChannelProcessor</code> can support the
+     *         passed configuration attribute
+     */
+    public boolean supports(ConfigAttribute attribute);
+}

+ 117 - 0
core/src/main/java/org/acegisecurity/securechannel/InsecureChannelProcessor.java

@@ -0,0 +1,117 @@
+/* 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.ConfigAttribute;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.intercept.web.FilterInvocation;
+
+import org.springframework.beans.factory.InitializingBean;
+
+import java.io.IOException;
+
+import java.util.Iterator;
+
+import javax.servlet.ServletException;
+
+
+/**
+ * <p>
+ * Ensures channel security is inactive by review of
+ * <code>HttpServletRequest.isSecure()</code> responses.
+ * </p>
+ * 
+ * <P>
+ * The class responds to one case-sensitive keyword, {@link
+ * #getInsecureKeyword}. If this keyword is detected,
+ * <code>HttpServletRequest.isSecure()</code> is used to determine the channel
+ * security offered. If channel security is present, the configured
+ * <code>ChannelEntryPoint</code> is called. By default the entry point is
+ * {@link RetryWithHttpEntryPoint}.
+ * </p>
+ * 
+ * <P>
+ * The default <code>insecureKeyword</code> is
+ * <code>REQUIRES_INSECURE_CHANNEL</code>.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class InsecureChannelProcessor implements InitializingBean,
+    ChannelProcessor {
+    //~ Instance fields ========================================================
+
+    private ChannelEntryPoint entryPoint = new RetryWithHttpEntryPoint();
+    private String insecureKeyword = "REQUIRES_INSECURE_CHANNEL";
+
+    //~ Methods ================================================================
+
+    public void setEntryPoint(ChannelEntryPoint entryPoint) {
+        this.entryPoint = entryPoint;
+    }
+
+    public ChannelEntryPoint getEntryPoint() {
+        return entryPoint;
+    }
+
+    public void setInsecureKeyword(String secureKeyword) {
+        this.insecureKeyword = secureKeyword;
+    }
+
+    public String getInsecureKeyword() {
+        return insecureKeyword;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        if ((insecureKeyword == null) || "".equals(insecureKeyword)) {
+            throw new IllegalArgumentException("insecureKeyword required");
+        }
+
+        if (entryPoint == null) {
+            throw new IllegalArgumentException("entryPoint required");
+        }
+    }
+
+    public void decide(FilterInvocation invocation,
+        ConfigAttributeDefinition config) throws IOException, ServletException {
+        if ((invocation == null) || (config == null)) {
+            throw new IllegalArgumentException("Nulls cannot be provided");
+        }
+
+        Iterator iter = config.getConfigAttributes();
+
+        while (iter.hasNext()) {
+            ConfigAttribute attribute = (ConfigAttribute) iter.next();
+
+            if (supports(attribute)) {
+                if (invocation.getHttpRequest().isSecure()) {
+                    entryPoint.commence(invocation.getRequest(),
+                        invocation.getResponse());
+                }
+            }
+        }
+    }
+
+    public boolean supports(ConfigAttribute attribute) {
+        if ((attribute != null) && (attribute.getAttribute() != null)
+            && attribute.getAttribute().equals(getInsecureKeyword())) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}

+ 0 - 50
core/src/main/java/org/acegisecurity/securechannel/InsecureChannelRequiredException.java

@@ -1,50 +0,0 @@
-/* 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);
-    }
-}

+ 116 - 0
core/src/main/java/org/acegisecurity/securechannel/SecureChannelProcessor.java

@@ -0,0 +1,116 @@
+/* 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.ConfigAttribute;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.intercept.web.FilterInvocation;
+
+import org.springframework.beans.factory.InitializingBean;
+
+import java.io.IOException;
+
+import java.util.Iterator;
+
+import javax.servlet.ServletException;
+
+
+/**
+ * <p>
+ * Ensures channel security is active by review of
+ * <code>HttpServletRequest.isSecure()</code> responses.
+ * </p>
+ * 
+ * <P>
+ * The class responds to one case-sensitive keyword, {@link #getSecureKeyword}.
+ * If this keyword is detected, <code>HttpServletRequest.isSecure()</code> is
+ * used to determine the channel security offered. If channel security is not
+ * present, the configured <code>ChannelEntryPoint</code> is called. By
+ * default the entry point is {@link RetryWithHttpsEntryPoint}.
+ * </p>
+ * 
+ * <P>
+ * The default <code>secureKeyword</code> is
+ * <code>REQUIRES_SECURE_CHANNEL</code>.
+ * </p>
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class SecureChannelProcessor implements InitializingBean,
+    ChannelProcessor {
+    //~ Instance fields ========================================================
+
+    private ChannelEntryPoint entryPoint = new RetryWithHttpsEntryPoint();
+    private String secureKeyword = "REQUIRES_SECURE_CHANNEL";
+
+    //~ Methods ================================================================
+
+    public void setEntryPoint(ChannelEntryPoint entryPoint) {
+        this.entryPoint = entryPoint;
+    }
+
+    public ChannelEntryPoint getEntryPoint() {
+        return entryPoint;
+    }
+
+    public void setSecureKeyword(String secureKeyword) {
+        this.secureKeyword = secureKeyword;
+    }
+
+    public String getSecureKeyword() {
+        return secureKeyword;
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        if ((secureKeyword == null) || "".equals(secureKeyword)) {
+            throw new IllegalArgumentException("secureKeyword required");
+        }
+
+        if (entryPoint == null) {
+            throw new IllegalArgumentException("entryPoint required");
+        }
+    }
+
+    public void decide(FilterInvocation invocation,
+        ConfigAttributeDefinition config) throws IOException, ServletException {
+        if ((invocation == null) || (config == null)) {
+            throw new IllegalArgumentException("Nulls cannot be provided");
+        }
+
+        Iterator iter = config.getConfigAttributes();
+
+        while (iter.hasNext()) {
+            ConfigAttribute attribute = (ConfigAttribute) iter.next();
+
+            if (supports(attribute)) {
+                if (!invocation.getHttpRequest().isSecure()) {
+                    entryPoint.commence(invocation.getRequest(),
+                        invocation.getResponse());
+                }
+            }
+        }
+    }
+
+    public boolean supports(ConfigAttribute attribute) {
+        if ((attribute != null) && (attribute.getAttribute() != null)
+            && attribute.getAttribute().equals(getSecureKeyword())) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+}

+ 0 - 50
core/src/main/java/org/acegisecurity/securechannel/SecureChannelRequiredException.java

@@ -1,50 +0,0 @@
-/* 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 required, but is not present.
- *
- * @author Ben Alex
- * @version $Id$
- */
-public class SecureChannelRequiredException extends AccessDeniedException {
-    //~ Constructors ===========================================================
-
-    /**
-     * Constructs a <code>SecureChannelRequiredException</code> with the
-     * specified message.
-     *
-     * @param msg the detail message.
-     */
-    public SecureChannelRequiredException(String msg) {
-        super(msg);
-    }
-
-    /**
-     * Constructs a <code>SecureChannelRequiredException</code> with the
-     * specified message and root cause.
-     *
-     * @param msg the detail message.
-     * @param t root cause
-     */
-    public SecureChannelRequiredException(String msg, Throwable t) {
-        super(msg, t);
-    }
-}

+ 5 - 1
core/src/test/java/org/acegisecurity/MockHttpServletResponse.java

@@ -56,7 +56,11 @@ public class MockHttpServletResponse implements HttpServletResponse {
     }
     }
 
 
     public boolean isCommitted() {
     public boolean isCommitted() {
-        throw new UnsupportedOperationException("mock method not implemented");
+        if (redirect == null) {
+            return false;
+        } else {
+            return true;
+        }
     }
     }
 
 
     public void setContentLength(int arg0) {
     public void setContentLength(int arg0) {

+ 129 - 86
core/src/test/java/org/acegisecurity/securechannel/ChannelDecisionManagerImplTests.java

@@ -17,6 +17,7 @@ package net.sf.acegisecurity.securechannel;
 
 
 import junit.framework.TestCase;
 import junit.framework.TestCase;
 
 
+import net.sf.acegisecurity.ConfigAttribute;
 import net.sf.acegisecurity.ConfigAttributeDefinition;
 import net.sf.acegisecurity.ConfigAttributeDefinition;
 import net.sf.acegisecurity.MockFilterChain;
 import net.sf.acegisecurity.MockFilterChain;
 import net.sf.acegisecurity.MockHttpServletRequest;
 import net.sf.acegisecurity.MockHttpServletRequest;
@@ -24,6 +25,14 @@ import net.sf.acegisecurity.MockHttpServletResponse;
 import net.sf.acegisecurity.SecurityConfig;
 import net.sf.acegisecurity.SecurityConfig;
 import net.sf.acegisecurity.intercept.web.FilterInvocation;
 import net.sf.acegisecurity.intercept.web.FilterInvocation;
 
 
+import java.io.IOException;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import javax.servlet.ServletException;
+
 
 
 /**
 /**
  * Tests {@link ChannelDecisionManagerImpl}.
  * Tests {@link ChannelDecisionManagerImpl}.
@@ -42,141 +51,175 @@ public class ChannelDecisionManagerImplTests extends TestCase {
         junit.textui.TestRunner.run(ChannelDecisionManagerImplTests.class);
         junit.textui.TestRunner.run(ChannelDecisionManagerImplTests.class);
     }
     }
 
 
-    public void testDetectsInvalidInsecureKeyword() throws Exception {
+    public void testCannotSetEmptyChannelProcessorsList()
+        throws Exception {
         ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
         ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
-        cdm.setInsecureKeyword("");
 
 
         try {
         try {
-            cdm.afterPropertiesSet();
+            cdm.setChannelProcessors(new Vector());
             fail("Should have thrown IllegalArgumentException");
             fail("Should have thrown IllegalArgumentException");
         } catch (IllegalArgumentException expected) {
         } catch (IllegalArgumentException expected) {
-            assertEquals("insecureKeyword required", expected.getMessage());
+            assertEquals("A list of ChannelProcessors is required",
+                expected.getMessage());
         }
         }
+    }
 
 
-        cdm.setInsecureKeyword(null);
+    public void testCannotSetIncorrectObjectTypesIntoChannelProcessorsList()
+        throws Exception {
+        ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
+        List list = new Vector();
+        list.add("THIS IS NOT A CHANNELPROCESSOR");
 
 
         try {
         try {
-            cdm.afterPropertiesSet();
+            cdm.setChannelProcessors(list);
             fail("Should have thrown IllegalArgumentException");
             fail("Should have thrown IllegalArgumentException");
         } catch (IllegalArgumentException expected) {
         } catch (IllegalArgumentException expected) {
-            assertEquals("insecureKeyword required", expected.getMessage());
+            assertTrue(true);
         }
         }
     }
     }
 
 
-    public void testDetectsInvalidSecureKeyword() throws Exception {
+    public void testCannotSetNullChannelProcessorsList()
+        throws Exception {
         ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
         ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
-        cdm.setSecureKeyword("");
 
 
         try {
         try {
-            cdm.afterPropertiesSet();
+            cdm.setChannelProcessors(null);
             fail("Should have thrown IllegalArgumentException");
             fail("Should have thrown IllegalArgumentException");
         } catch (IllegalArgumentException expected) {
         } catch (IllegalArgumentException expected) {
-            assertEquals("secureKeyword required", expected.getMessage());
+            assertEquals("A list of ChannelProcessors is required",
+                expected.getMessage());
         }
         }
+    }
 
 
-        cdm.setSecureKeyword(null);
+    public void testDecideIsOperational() throws Exception {
+        ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
+        MockChannelProcessor cpXyz = new MockChannelProcessor("xyz", false);
+        MockChannelProcessor cpAbc = new MockChannelProcessor("abc", true);
+        List list = new Vector();
+        list.add(cpXyz);
+        list.add(cpAbc);
+        cdm.setChannelProcessors(list);
+        cdm.afterPropertiesSet();
 
 
-        try {
-            cdm.afterPropertiesSet();
-            fail("Should have thrown IllegalArgumentException");
-        } catch (IllegalArgumentException expected) {
-            assertEquals("secureKeyword required", expected.getMessage());
-        }
+        MockHttpServletRequest request = new MockHttpServletRequest("not used");
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockFilterChain chain = new MockFilterChain();
+        FilterInvocation fi = new FilterInvocation(request, response, chain);
+
+        ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
+        cad.addConfigAttribute(new SecurityConfig("xyz"));
+
+        cdm.decide(fi, cad);
+        assertTrue(fi.getResponse().isCommitted());
     }
     }
 
 
-    public void testDetectsNullsPassedToMainMethod() {
+    public void testDecideIteratesAllProcessorsIfNoneCommitAResponse()
+        throws Exception {
         ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
         ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
+        MockChannelProcessor cpXyz = new MockChannelProcessor("xyz", false);
+        MockChannelProcessor cpAbc = new MockChannelProcessor("abc", false);
+        List list = new Vector();
+        list.add(cpXyz);
+        list.add(cpAbc);
+        cdm.setChannelProcessors(list);
+        cdm.afterPropertiesSet();
 
 
-        try {
-            cdm.decide(null, new ConfigAttributeDefinition());
-            fail("Should have thrown IllegalArgumentException");
-        } catch (IllegalArgumentException expected) {
-            assertEquals("Nulls cannot be provided", expected.getMessage());
-        }
+        MockHttpServletRequest request = new MockHttpServletRequest("not used");
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockFilterChain chain = new MockFilterChain();
+        FilterInvocation fi = new FilterInvocation(request, response, chain);
 
 
-        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());
-        }
+        ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
+        cad.addConfigAttribute(new SecurityConfig(
+                "SOME_ATTRIBUTE_NO_PROCESSORS_SUPPORT"));
+
+        cdm.decide(fi, cad);
+        assertFalse(fi.getResponse().isCommitted());
     }
     }
 
 
-    public void testDetectsWhenInsecureChannelNeededAndInsecureSchemeUsed() {
-        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
-        attr.addConfigAttribute(new SecurityConfig(
-                "SOME_CONFIG_ATTRIBUTE_TO_IGNORE"));
-        attr.addConfigAttribute(new SecurityConfig("REQUIRES_INSECURE_CHANNEL"));
+    public void testDelegatesSupports() throws Exception {
+        ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
+        MockChannelProcessor cpXyz = new MockChannelProcessor("xyz", false);
+        MockChannelProcessor cpAbc = new MockChannelProcessor("abc", false);
+        List list = new Vector();
+        list.add(cpXyz);
+        list.add(cpAbc);
+        cdm.setChannelProcessors(list);
+        cdm.afterPropertiesSet();
 
 
-        MockHttpServletRequest request = new MockHttpServletRequest("foo=bar");
-        request.setScheme("http");
+        assertTrue(cdm.supports(new SecurityConfig("xyz")));
+        assertTrue(cdm.supports(new SecurityConfig("abc")));
+        assertFalse(cdm.supports(new SecurityConfig("UNSUPPORTED")));
+    }
 
 
+    public void testGettersSetters() {
         ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
         ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
-        cdm.decide(new FilterInvocation(request, new MockHttpServletResponse(),
-                new MockFilterChain()), attr);
-        assertTrue(true);
-    }
+        assertNull(cdm.getChannelProcessors());
 
 
-    public void testDetectsWhenInsecureChannelNeededAndSecureSchemeUsed() {
-        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
-        attr.addConfigAttribute(new SecurityConfig(
-                "SOME_CONFIG_ATTRIBUTE_TO_IGNORE"));
-        attr.addConfigAttribute(new SecurityConfig("REQUIRES_INSECURE_CHANNEL"));
+        MockChannelProcessor cpXyz = new MockChannelProcessor("xyz", false);
+        MockChannelProcessor cpAbc = new MockChannelProcessor("abc", false);
+        List list = new Vector();
+        list.add(cpXyz);
+        list.add(cpAbc);
+        cdm.setChannelProcessors(list);
 
 
-        MockHttpServletRequest request = new MockHttpServletRequest("foo=bar");
-        request.setScheme("https");
+        assertEquals(list, cdm.getChannelProcessors());
+    }
 
 
+    public void testStartupFailsWithEmptyChannelProcessorsList()
+        throws Exception {
         ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
         ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
 
 
         try {
         try {
-            cdm.decide(new FilterInvocation(request,
-                    new MockHttpServletResponse(), new MockFilterChain()), attr);
-        } catch (InsecureChannelRequiredException expected) {
-            assertTrue(true);
+            cdm.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("A list of ChannelProcessors is required",
+                expected.getMessage());
         }
         }
     }
     }
 
 
-    public void testDetectsWhenSecureChannelNeeded() {
-        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
-        attr.addConfigAttribute(new SecurityConfig(
-                "SOME_CONFIG_ATTRIBUTE_TO_IGNORE"));
-        attr.addConfigAttribute(new SecurityConfig("REQUIRES_SECURE_CHANNEL"));
+    //~ Inner Classes ==========================================================
 
 
-        MockHttpServletRequest request = new MockHttpServletRequest("foo=bar");
-        request.setScheme("http");
+    private class MockChannelProcessor implements ChannelProcessor {
+        private String configAttribute;
+        private boolean failIfCalled;
 
 
-        ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
+        public MockChannelProcessor(String configAttribute, boolean failIfCalled) {
+            this.configAttribute = configAttribute;
+            this.failIfCalled = failIfCalled;
+        }
 
 
-        try {
-            cdm.decide(new FilterInvocation(request,
-                    new MockHttpServletResponse(), new MockFilterChain()), attr);
-        } catch (SecureChannelRequiredException expected) {
-            assertTrue(true);
+        private MockChannelProcessor() {
+            super();
         }
         }
-    }
 
 
-    public void testGetterSetters() throws Exception {
-        ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
-        cdm.afterPropertiesSet();
-        assertEquals("REQUIRES_INSECURE_CHANNEL", cdm.getInsecureKeyword());
-        assertEquals("REQUIRES_SECURE_CHANNEL", cdm.getSecureKeyword());
+        public void decide(FilterInvocation invocation,
+            ConfigAttributeDefinition config)
+            throws IOException, ServletException {
+            Iterator iter = config.getConfigAttributes();
 
 
-        cdm.setInsecureKeyword("MY_INSECURE");
-        cdm.setSecureKeyword("MY_SECURE");
+            if (failIfCalled) {
+                fail("Should not have called this channel processor");
+            }
 
 
-        assertEquals("MY_INSECURE", cdm.getInsecureKeyword());
-        assertEquals("MY_SECURE", cdm.getSecureKeyword());
-    }
+            while (iter.hasNext()) {
+                ConfigAttribute attr = (ConfigAttribute) iter.next();
 
 
-    public void testIgnoresOtherConfigAttributes() {
-        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
-        attr.addConfigAttribute(new SecurityConfig("XYZ"));
+                if (attr.equals(configAttribute)) {
+                    invocation.getHttpResponse().sendRedirect("/redirected");
 
 
-        ChannelDecisionManagerImpl cdm = new ChannelDecisionManagerImpl();
-        cdm.decide(new FilterInvocation(new MockHttpServletRequest("x"),
-                new MockHttpServletResponse(), new MockFilterChain()), attr);
-        assertTrue(true);
+                    return;
+                }
+            }
+        }
+
+        public boolean supports(ConfigAttribute attribute) {
+            if (attribute.getAttribute().equals(configAttribute)) {
+                return true;
+            } else {
+                return false;
+            }
+        }
     }
     }
 }
 }
-;

+ 139 - 131
core/src/test/java/org/acegisecurity/securechannel/ChannelProcessingFilterTests.java

@@ -17,18 +17,19 @@ package net.sf.acegisecurity.securechannel;
 
 
 import junit.framework.TestCase;
 import junit.framework.TestCase;
 
 
+import net.sf.acegisecurity.ConfigAttribute;
 import net.sf.acegisecurity.ConfigAttributeDefinition;
 import net.sf.acegisecurity.ConfigAttributeDefinition;
-import net.sf.acegisecurity.MockFilterConfig;
 import net.sf.acegisecurity.MockHttpServletRequest;
 import net.sf.acegisecurity.MockHttpServletRequest;
 import net.sf.acegisecurity.MockHttpServletResponse;
 import net.sf.acegisecurity.MockHttpServletResponse;
 import net.sf.acegisecurity.SecurityConfig;
 import net.sf.acegisecurity.SecurityConfig;
 import net.sf.acegisecurity.intercept.web.FilterInvocation;
 import net.sf.acegisecurity.intercept.web.FilterInvocation;
 import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSource;
 import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSource;
-import net.sf.acegisecurity.intercept.web.RegExpBasedFilterInvocationDefinitionMap;
 
 
 import java.io.IOException;
 import java.io.IOException;
 
 
 import java.util.Iterator;
 import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
 
 
 import javax.servlet.FilterChain;
 import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
 import javax.servlet.ServletException;
@@ -53,61 +54,16 @@ public class ChannelProcessingFilterTests extends TestCase {
         junit.textui.TestRunner.run(ChannelProcessingFilterTests.class);
         junit.textui.TestRunner.run(ChannelProcessingFilterTests.class);
     }
     }
 
 
-    public void testCallsInsecureEntryPointWhenTooMuchChannelSecurity()
+    public void testDetectsMissingChannelDecisionManager()
         throws Exception {
         throws Exception {
-        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
-        attr.addConfigAttribute(new SecurityConfig("REQUIRES_INSECURE_CHANNEL"));
-
-        MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path",
-                attr);
-
         ChannelProcessingFilter filter = new ChannelProcessingFilter();
         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();
         ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
-        attr.addConfigAttribute(new SecurityConfig("REQUIRES_SECURE_CHANNEL"));
+        attr.addConfigAttribute(new SecurityConfig("MOCK"));
 
 
         MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path",
         MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path",
-                attr);
-
-        ChannelProcessingFilter filter = new ChannelProcessingFilter();
-        filter.setInsecureChannelEntryPoint(new MockEntryPoint(false));
-        filter.setSecureChannelEntryPoint(new MockEntryPoint(true));
+                attr, true);
         filter.setFilterInvocationDefinitionSource(fids);
         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 {
         try {
             filter.afterPropertiesSet();
             filter.afterPropertiesSet();
@@ -118,12 +74,11 @@ public class ChannelProcessingFilterTests extends TestCase {
         }
         }
     }
     }
 
 
-    public void testDetectsMissingFilterInvocationDefinitionMap()
+    public void testDetectsMissingFilterInvocationDefinitionSource()
         throws Exception {
         throws Exception {
         ChannelProcessingFilter filter = new ChannelProcessingFilter();
         ChannelProcessingFilter filter = new ChannelProcessingFilter();
-        filter.setInsecureChannelEntryPoint(new RetryWithHttpEntryPoint());
-        filter.setSecureChannelEntryPoint(new RetryWithHttpsEntryPoint());
-        filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
+        filter.setChannelDecisionManager(new MockChannelDecisionManager(false,
+                "MOCK"));
 
 
         try {
         try {
             filter.afterPropertiesSet();
             filter.afterPropertiesSet();
@@ -134,81 +89,86 @@ public class ChannelProcessingFilterTests extends TestCase {
         }
         }
     }
     }
 
 
-    public void testDetectsMissingInsecureChannelEntryPoint()
-        throws Exception {
+    public void testDetectsSupportedConfigAttribute() throws Exception {
         ChannelProcessingFilter filter = new ChannelProcessingFilter();
         ChannelProcessingFilter filter = new ChannelProcessingFilter();
-        filter.setSecureChannelEntryPoint(new RetryWithHttpsEntryPoint());
-        filter.setFilterInvocationDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
-        filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
+        filter.setChannelDecisionManager(new MockChannelDecisionManager(false,
+                "SUPPORTS_MOCK_ONLY"));
 
 
-        try {
-            filter.afterPropertiesSet();
-            fail("Should have thrown IllegalArgumentException");
-        } catch (IllegalArgumentException expected) {
-            assertEquals("insecureChannelEntryPoint must be specified",
-                expected.getMessage());
-        }
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("SUPPORTS_MOCK_ONLY"));
+
+        MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path",
+                attr, true);
+
+        filter.setFilterInvocationDefinitionSource(fids);
+
+        filter.afterPropertiesSet();
+        assertTrue(true);
     }
     }
 
 
-    public void testDetectsMissingSecureChannelEntryPoint()
+    public void testDetectsUnsupportedConfigAttribute()
         throws Exception {
         throws Exception {
         ChannelProcessingFilter filter = new ChannelProcessingFilter();
         ChannelProcessingFilter filter = new ChannelProcessingFilter();
-        filter.setInsecureChannelEntryPoint(new RetryWithHttpEntryPoint());
-        filter.setFilterInvocationDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
-        filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
+        filter.setChannelDecisionManager(new MockChannelDecisionManager(false,
+                "SUPPORTS_MOCK_ONLY"));
+
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("SUPPORTS_MOCK_ONLY"));
+        attr.addConfigAttribute(new SecurityConfig("INVALID_ATTRIBUTE"));
+
+        MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path",
+                attr, true);
+
+        filter.setFilterInvocationDefinitionSource(fids);
 
 
         try {
         try {
             filter.afterPropertiesSet();
             filter.afterPropertiesSet();
             fail("Should have thrown IllegalArgumentException");
             fail("Should have thrown IllegalArgumentException");
         } catch (IllegalArgumentException expected) {
         } catch (IllegalArgumentException expected) {
-            assertEquals("secureChannelEntryPoint must be specified",
-                expected.getMessage());
+            assertTrue(expected.getMessage().startsWith("Unsupported configuration attributes:"));
         }
         }
     }
     }
 
 
-    public void testDoFilterWithNonHttpServletRequestDetected()
+    public void testDoFilterWhenManagerDoesCommitResponse()
         throws Exception {
         throws Exception {
         ChannelProcessingFilter filter = new ChannelProcessingFilter();
         ChannelProcessingFilter filter = new ChannelProcessingFilter();
+        filter.setChannelDecisionManager(new MockChannelDecisionManager(true,
+                "SOME_ATTRIBUTE"));
 
 
-        try {
-            filter.doFilter(null, new MockHttpServletResponse(),
-                new MockFilterChain());
-            fail("Should have thrown ServletException");
-        } catch (ServletException expected) {
-            assertEquals("HttpServletRequest required", expected.getMessage());
-        }
-    }
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("SOME_ATTRIBUTE"));
 
 
-    public void testDoFilterWithNonHttpServletResponseDetected()
-        throws Exception {
-        ChannelProcessingFilter filter = new ChannelProcessingFilter();
+        MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path",
+                attr, true);
 
 
-        try {
-            filter.doFilter(new MockHttpServletRequest(null, null), null,
-                new MockFilterChain());
-            fail("Should have thrown ServletException");
-        } catch (ServletException expected) {
-            assertEquals("HttpServletResponse required", expected.getMessage());
-        }
+        filter.setFilterInvocationDefinitionSource(fids);
+
+        MockHttpServletRequest request = new MockHttpServletRequest("info=now");
+        request.setServletPath("/path");
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockFilterChain chain = new MockFilterChain(false);
+
+        filter.doFilter(request, response, chain);
+        assertTrue(true);
     }
     }
 
 
-    public void testDoesNotInterruptRequestsWithCorrectChannelSecurity()
+    public void testDoFilterWhenManagerDoesNotCommitResponse()
         throws Exception {
         throws Exception {
+        ChannelProcessingFilter filter = new ChannelProcessingFilter();
+        filter.setChannelDecisionManager(new MockChannelDecisionManager(false,
+                "SOME_ATTRIBUTE"));
+
         ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
         ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
-        attr.addConfigAttribute(new SecurityConfig("REQUIRES_SECURE_CHANNEL"));
+        attr.addConfigAttribute(new SecurityConfig("SOME_ATTRIBUTE"));
 
 
         MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path",
         MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path",
-                attr);
+                attr, true);
 
 
-        ChannelProcessingFilter filter = new ChannelProcessingFilter();
-        filter.setInsecureChannelEntryPoint(new RetryWithHttpEntryPoint());
-        filter.setSecureChannelEntryPoint(new RetryWithHttpsEntryPoint());
         filter.setFilterInvocationDefinitionSource(fids);
         filter.setFilterInvocationDefinitionSource(fids);
-        filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
 
 
         MockHttpServletRequest request = new MockHttpServletRequest("info=now");
         MockHttpServletRequest request = new MockHttpServletRequest("info=now");
         request.setServletPath("/path");
         request.setServletPath("/path");
-        request.setScheme("https");
 
 
         MockHttpServletResponse response = new MockHttpServletResponse();
         MockHttpServletResponse response = new MockHttpServletResponse();
         MockFilterChain chain = new MockFilterChain(true);
         MockFilterChain chain = new MockFilterChain(true);
@@ -217,15 +177,23 @@ public class ChannelProcessingFilterTests extends TestCase {
         assertTrue(true);
         assertTrue(true);
     }
     }
 
 
-    public void testDoesNotInterruptRequestsWithNoConfigAttribute()
+    public void testDoFilterWhenNullConfigAttributeReturned()
         throws Exception {
         throws Exception {
         ChannelProcessingFilter filter = new ChannelProcessingFilter();
         ChannelProcessingFilter filter = new ChannelProcessingFilter();
-        filter.setInsecureChannelEntryPoint(new RetryWithHttpEntryPoint());
-        filter.setSecureChannelEntryPoint(new RetryWithHttpsEntryPoint());
-        filter.setFilterInvocationDefinitionSource(new RegExpBasedFilterInvocationDefinitionMap());
-        filter.setChannelDecisionManager(new ChannelDecisionManagerImpl());
+        filter.setChannelDecisionManager(new MockChannelDecisionManager(false,
+                "NOT_USED"));
+
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("NOT_USED"));
+
+        MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path",
+                attr, true);
+
+        filter.setFilterInvocationDefinitionSource(fids);
 
 
         MockHttpServletRequest request = new MockHttpServletRequest("info=now");
         MockHttpServletRequest request = new MockHttpServletRequest("info=now");
+        request.setServletPath("/PATH_NOT_MATCHING_CONFIG_ATTRIBUTE");
+
         MockHttpServletResponse response = new MockHttpServletResponse();
         MockHttpServletResponse response = new MockHttpServletResponse();
         MockFilterChain chain = new MockFilterChain(true);
         MockFilterChain chain = new MockFilterChain(true);
 
 
@@ -233,50 +201,81 @@ public class ChannelProcessingFilterTests extends TestCase {
         assertTrue(true);
         assertTrue(true);
     }
     }
 
 
-    public void testGetterSetters() {
+    public void testDoFilterWithNonHttpServletRequestDetected()
+        throws Exception {
         ChannelProcessingFilter filter = new ChannelProcessingFilter();
         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);
+        try {
+            filter.doFilter(null, new MockHttpServletResponse(),
+                new MockFilterChain());
+            fail("Should have thrown ServletException");
+        } catch (ServletException expected) {
+            assertEquals("HttpServletRequest required", expected.getMessage());
+        }
     }
     }
 
 
-    public void testLifecycle() throws Exception {
+    public void testDoFilterWithNonHttpServletResponseDetected()
+        throws Exception {
         ChannelProcessingFilter filter = new ChannelProcessingFilter();
         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());
+        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 testGetterSetters() throws Exception {
+        ChannelProcessingFilter filter = new ChannelProcessingFilter();
+        filter.setChannelDecisionManager(new MockChannelDecisionManager(false,
+                "MOCK"));
+        assertTrue(filter.getChannelDecisionManager() != null);
+
+        ConfigAttributeDefinition attr = new ConfigAttributeDefinition();
+        attr.addConfigAttribute(new SecurityConfig("MOCK"));
+
+        MockFilterInvocationDefinitionMap fids = new MockFilterInvocationDefinitionMap("/path",
+                attr, false);
+
+        filter.setFilterInvocationDefinitionSource(fids);
+        assertTrue(filter.getFilterInvocationDefinitionSource() != null);
+
+        filter.init(null);
+        filter.afterPropertiesSet();
         filter.destroy();
         filter.destroy();
     }
     }
 
 
     //~ Inner Classes ==========================================================
     //~ Inner Classes ==========================================================
 
 
-    private class MockEntryPoint implements ChannelEntryPoint {
-        private boolean expectToBeCalled;
+    private class MockChannelDecisionManager implements ChannelDecisionManager {
+        private String supportAttribute;
+        private boolean commitAResponse;
 
 
-        public MockEntryPoint(boolean expectToBeCalled) {
-            this.expectToBeCalled = expectToBeCalled;
+        public MockChannelDecisionManager(boolean commitAResponse,
+            String supportAttribute) {
+            this.commitAResponse = commitAResponse;
+            this.supportAttribute = supportAttribute;
         }
         }
 
 
-        private MockEntryPoint() {
+        private MockChannelDecisionManager() {
             super();
             super();
         }
         }
 
 
-        public void commence(ServletRequest request, ServletResponse response)
+        public void decide(FilterInvocation invocation,
+            ConfigAttributeDefinition config)
             throws IOException, ServletException {
             throws IOException, ServletException {
-            if (expectToBeCalled) {
-                assertTrue(true);
+            if (commitAResponse) {
+                invocation.getHttpResponse().sendRedirect("/redirected");
+            }
+        }
+
+        public boolean supports(ConfigAttribute attribute) {
+            if (attribute.getAttribute().equals(supportAttribute)) {
+                return true;
             } else {
             } else {
-                fail("Did not expect this ChannelEntryPoint to be called");
+                return false;
             }
             }
         }
         }
     }
     }
@@ -306,11 +305,13 @@ public class ChannelProcessingFilterTests extends TestCase {
         implements FilterInvocationDefinitionSource {
         implements FilterInvocationDefinitionSource {
         private ConfigAttributeDefinition toReturn;
         private ConfigAttributeDefinition toReturn;
         private String servletPath;
         private String servletPath;
+        private boolean provideIterator;
 
 
         public MockFilterInvocationDefinitionMap(String servletPath,
         public MockFilterInvocationDefinitionMap(String servletPath,
-            ConfigAttributeDefinition toReturn) {
+            ConfigAttributeDefinition toReturn, boolean provideIterator) {
             this.servletPath = servletPath;
             this.servletPath = servletPath;
             this.toReturn = toReturn;
             this.toReturn = toReturn;
+            this.provideIterator = provideIterator;
         }
         }
 
 
         private MockFilterInvocationDefinitionMap() {
         private MockFilterInvocationDefinitionMap() {
@@ -329,7 +330,14 @@ public class ChannelProcessingFilterTests extends TestCase {
         }
         }
 
 
         public Iterator getConfigAttributeDefinitions() {
         public Iterator getConfigAttributeDefinitions() {
-            return null;
+            if (!provideIterator) {
+                return null;
+            }
+
+            List list = new Vector();
+            list.add(toReturn);
+
+            return list.iterator();
         }
         }
 
 
         public boolean supports(Class clazz) {
         public boolean supports(Class clazz) {

+ 153 - 0
core/src/test/java/org/acegisecurity/securechannel/InsecureChannelProcessorTests.java

@@ -0,0 +1,153 @@
+/* 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 InsecureChannelProcessor}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class InsecureChannelProcessorTests extends TestCase {
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(InsecureChannelProcessorTests.class);
+    }
+
+    public void testDecideDetectsAcceptableChannel() throws Exception {
+        ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
+        cad.addConfigAttribute(new SecurityConfig("SOME_IGNORED_ATTRIBUTE"));
+        cad.addConfigAttribute(new SecurityConfig("REQUIRES_INSECURE_CHANNEL"));
+
+        MockHttpServletRequest request = new MockHttpServletRequest("info=true");
+        request.setServerName("localhost");
+        request.setContextPath("/bigapp");
+        request.setServletPath("/servlet");
+        request.setScheme("http");
+        request.setServerPort(8080);
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockFilterChain chain = new MockFilterChain();
+        FilterInvocation fi = new FilterInvocation(request, response, chain);
+
+        InsecureChannelProcessor processor = new InsecureChannelProcessor();
+        processor.decide(fi, cad);
+
+        assertFalse(fi.getResponse().isCommitted());
+    }
+
+    public void testDecideDetectsUnacceptableChannel()
+        throws Exception {
+        ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
+        cad.addConfigAttribute(new SecurityConfig("SOME_IGNORED_ATTRIBUTE"));
+        cad.addConfigAttribute(new SecurityConfig("REQUIRES_INSECURE_CHANNEL"));
+
+        MockHttpServletRequest request = new MockHttpServletRequest("info=true");
+        request.setServerName("localhost");
+        request.setContextPath("/bigapp");
+        request.setServletPath("/servlet");
+        request.setScheme("https");
+        request.setServerPort(8443);
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockFilterChain chain = new MockFilterChain();
+        FilterInvocation fi = new FilterInvocation(request, response, chain);
+
+        InsecureChannelProcessor processor = new InsecureChannelProcessor();
+        processor.decide(fi, cad);
+
+        assertTrue(fi.getResponse().isCommitted());
+    }
+
+    public void testDecideRejectsNulls() throws Exception {
+        InsecureChannelProcessor processor = new InsecureChannelProcessor();
+        processor.afterPropertiesSet();
+
+        try {
+            processor.decide(null, null);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testGettersSetters() {
+        InsecureChannelProcessor processor = new InsecureChannelProcessor();
+        assertEquals("REQUIRES_INSECURE_CHANNEL", processor.getInsecureKeyword());
+        processor.setInsecureKeyword("X");
+        assertEquals("X", processor.getInsecureKeyword());
+
+        assertTrue(processor.getEntryPoint() != null);
+        processor.setEntryPoint(null);
+        assertTrue(processor.getEntryPoint() == null);
+    }
+
+    public void testMissingEntryPoint() throws Exception {
+        InsecureChannelProcessor processor = new InsecureChannelProcessor();
+        processor.setEntryPoint(null);
+
+        try {
+            processor.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("entryPoint required", expected.getMessage());
+        }
+    }
+
+    public void testMissingSecureChannelKeyword() throws Exception {
+        InsecureChannelProcessor processor = new InsecureChannelProcessor();
+        processor.setInsecureKeyword(null);
+
+        try {
+            processor.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("insecureKeyword required", expected.getMessage());
+        }
+
+        processor.setInsecureKeyword("");
+
+        try {
+            processor.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("insecureKeyword required", expected.getMessage());
+        }
+    }
+
+    public void testSupports() {
+        InsecureChannelProcessor processor = new InsecureChannelProcessor();
+        assertTrue(processor.supports(
+                new SecurityConfig("REQUIRES_INSECURE_CHANNEL")));
+        assertFalse(processor.supports(null));
+        assertFalse(processor.supports(new SecurityConfig("NOT_SUPPORTED")));
+    }
+}

+ 153 - 0
core/src/test/java/org/acegisecurity/securechannel/SecureChannelProcessorTests.java

@@ -0,0 +1,153 @@
+/* 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 SecureChannelProcessor}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class SecureChannelProcessorTests extends TestCase {
+    //~ Methods ================================================================
+
+    public final void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(SecureChannelProcessorTests.class);
+    }
+
+    public void testDecideDetectsAcceptableChannel() throws Exception {
+        ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
+        cad.addConfigAttribute(new SecurityConfig("SOME_IGNORED_ATTRIBUTE"));
+        cad.addConfigAttribute(new SecurityConfig("REQUIRES_SECURE_CHANNEL"));
+
+        MockHttpServletRequest request = new MockHttpServletRequest("info=true");
+        request.setServerName("localhost");
+        request.setContextPath("/bigapp");
+        request.setServletPath("/servlet");
+        request.setScheme("https");
+        request.setServerPort(8443);
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockFilterChain chain = new MockFilterChain();
+        FilterInvocation fi = new FilterInvocation(request, response, chain);
+
+        SecureChannelProcessor processor = new SecureChannelProcessor();
+        processor.decide(fi, cad);
+
+        assertFalse(fi.getResponse().isCommitted());
+    }
+
+    public void testDecideDetectsUnacceptableChannel()
+        throws Exception {
+        ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
+        cad.addConfigAttribute(new SecurityConfig("SOME_IGNORED_ATTRIBUTE"));
+        cad.addConfigAttribute(new SecurityConfig("REQUIRES_SECURE_CHANNEL"));
+
+        MockHttpServletRequest request = new MockHttpServletRequest("info=true");
+        request.setServerName("localhost");
+        request.setContextPath("/bigapp");
+        request.setServletPath("/servlet");
+        request.setScheme("http");
+        request.setServerPort(8080);
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockFilterChain chain = new MockFilterChain();
+        FilterInvocation fi = new FilterInvocation(request, response, chain);
+
+        SecureChannelProcessor processor = new SecureChannelProcessor();
+        processor.decide(fi, cad);
+
+        assertTrue(fi.getResponse().isCommitted());
+    }
+
+    public void testDecideRejectsNulls() throws Exception {
+        SecureChannelProcessor processor = new SecureChannelProcessor();
+        processor.afterPropertiesSet();
+
+        try {
+            processor.decide(null, null);
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertTrue(true);
+        }
+    }
+
+    public void testGettersSetters() {
+        SecureChannelProcessor processor = new SecureChannelProcessor();
+        assertEquals("REQUIRES_SECURE_CHANNEL", processor.getSecureKeyword());
+        processor.setSecureKeyword("X");
+        assertEquals("X", processor.getSecureKeyword());
+
+        assertTrue(processor.getEntryPoint() != null);
+        processor.setEntryPoint(null);
+        assertTrue(processor.getEntryPoint() == null);
+    }
+
+    public void testMissingEntryPoint() throws Exception {
+        SecureChannelProcessor processor = new SecureChannelProcessor();
+        processor.setEntryPoint(null);
+
+        try {
+            processor.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("entryPoint required", expected.getMessage());
+        }
+    }
+
+    public void testMissingSecureChannelKeyword() throws Exception {
+        SecureChannelProcessor processor = new SecureChannelProcessor();
+        processor.setSecureKeyword(null);
+
+        try {
+            processor.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("secureKeyword required", expected.getMessage());
+        }
+
+        processor.setSecureKeyword("");
+
+        try {
+            processor.afterPropertiesSet();
+            fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            assertEquals("secureKeyword required", expected.getMessage());
+        }
+    }
+
+    public void testSupports() {
+        SecureChannelProcessor processor = new SecureChannelProcessor();
+        assertTrue(processor.supports(
+                new SecurityConfig("REQUIRES_SECURE_CHANNEL")));
+        assertFalse(processor.supports(null));
+        assertFalse(processor.supports(new SecurityConfig("NOT_SUPPORTED")));
+    }
+}

+ 71 - 59
docs/reference/src/index.xml

@@ -522,20 +522,17 @@
         Acegi Security System for Spring use this class. Refer to the Filters
         Acegi Security System for Spring use this class. Refer to the Filters
         section to learn more about this bean.</para>
         section to learn more about this bean.</para>
 
 
-        <para>In the application context you will need to configure four
+        <para>In the application context you will need to configure three
         beans:</para>
         beans:</para>
 
 
         <programlisting>&lt;bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter"&gt;
         <programlisting>&lt;bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter"&gt;
   &lt;property name="filterSecurityInterceptor"&gt;&lt;ref bean="filterInvocationInterceptor"/&gt;&lt;/property&gt;
   &lt;property name="filterSecurityInterceptor"&gt;&lt;ref bean="filterInvocationInterceptor"/&gt;&lt;/property&gt;
   &lt;property name="authenticationEntryPoint"&gt;&lt;ref bean="authenticationEntryPoint"/&gt;&lt;/property&gt;
   &lt;property name="authenticationEntryPoint"&gt;&lt;ref bean="authenticationEntryPoint"/&gt;&lt;/property&gt;
-  &lt;property name="portResolver"&gt;&lt;ref bean="portResolver"/&gt;&lt;/property&gt;
 &lt;/bean&gt;
 &lt;/bean&gt;
 
 
 &lt;bean id="authenticationEntryPoint" class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"&gt;
 &lt;bean id="authenticationEntryPoint" class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"&gt;
   &lt;property name="loginFormUrl"&gt;&lt;value&gt;/acegilogin.jsp&lt;/value&gt;&lt;/property&gt;
   &lt;property name="loginFormUrl"&gt;&lt;value&gt;/acegilogin.jsp&lt;/value&gt;&lt;/property&gt;
   &lt;property name="forceHttps"&gt;&lt;value&gt;false&lt;/value&gt;&lt;/property&gt;
   &lt;property name="forceHttps"&gt;&lt;value&gt;false&lt;/value&gt;&lt;/property&gt;
-  &lt;property name="portResolver"&gt;&lt;ref bean="portResolver"/&gt;&lt;/property&gt;
-  &lt;property name="portMapper"&gt;&lt;ref bean="portMapper"/&gt;&lt;/property&gt;
 &lt;/bean&gt;
 &lt;/bean&gt;
 
 
 &lt;bean id="filterInvocationInterceptor" class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor"&gt;
 &lt;bean id="filterInvocationInterceptor" class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor"&gt;
@@ -549,12 +546,6 @@
       \A/secure/.*\Z=ROLE_SUPERVISOR,ROLE_TELLER
       \A/secure/.*\Z=ROLE_SUPERVISOR,ROLE_TELLER
     &lt;/value&gt;
     &lt;/value&gt;
   &lt;/property&gt;
   &lt;/property&gt;
-&lt;/bean&gt;
-
-&lt;!-- Comment the always[Scheme]Port properties to use ServletRequest.getServerPort() --&gt;
-&lt;bean id="portResolver" class="net.sf.acegisecurity.util.PortResolverImpl"&gt;
-  &lt;property name="alwaysHttpPort"&gt;&lt;value&gt;8080&lt;/value&gt;&lt;/property&gt;
-  &lt;property name="alwaysHttpsPort"&gt;&lt;value&gt;8443&lt;/value&gt;&lt;/property&gt;
 &lt;/bean&gt;</programlisting>
 &lt;/bean&gt;</programlisting>
 
 
         <para>The <literal>AuthenticationEntryPoint</literal> will be called
         <para>The <literal>AuthenticationEntryPoint</literal> will be called
@@ -573,13 +564,6 @@
         properties related to forcing the use of HTTPS, so please refer to the
         properties related to forcing the use of HTTPS, so please refer to the
         JavaDocs if you require this.</para>
         JavaDocs if you require this.</para>
 
 
-        <para>The <literal>PortResolver</literal> is used to inspect a HTTP
-        request and determine the server port it was received on. Generally
-        this means using <literal>ServletRequest.getServerPort()</literal>,
-        although implementations can be forced to always return particular
-        ports (based on the transport protocol), as shown in the example
-        above. </para>
-
         <para>The <literal>PortMapper</literal> provides information on which
         <para>The <literal>PortMapper</literal> provides information on which
         HTTPS ports correspond to which HTTP ports. This is used by the
         HTTPS ports correspond to which HTTP ports. This is used by the
         <literal>AuthenticationProcessingFilterEntryPoint</literal> and
         <literal>AuthenticationProcessingFilterEntryPoint</literal> and
@@ -2756,10 +2740,12 @@ $CATALINA_HOME/bin/startup.sh</programlisting></para>
 
 
         <para>In addition to coordinating the authentication and authorization
         <para>In addition to coordinating the authentication and authorization
         requirements of your application, the Acegi Security System for Spring
         requirements of your application, the Acegi Security System for Spring
-        is also able to ensure web requests are received using an appropriate
-        transport. If your application has many security requirements, you'll
-        probably want to use HTTPS as the transport, whilst less secure pages
-        can use the unencrypted HTTP transport.</para>
+        is also able to ensure unauthenticated web requests have certain
+        properties. These properties may include being of a particular
+        transport type, having a particular <literal>HttpSession</literal>
+        attribute set and so on. The most common requirement is for your web
+        requests to be received using a particular transport protocol, such as
+        HTTPS.</para>
 
 
         <para>An important issue in considering transport security is that of
         <para>An important issue in considering transport security is that of
         session hijacking. Your web container manages a
         session hijacking. Your web container manages a
@@ -2809,30 +2795,28 @@ $CATALINA_HOME/bin/startup.sh</programlisting></para>
 
 
         <para><programlisting>&lt;bean id="channelProcessingFilter" class="net.sf.acegisecurity.securechannel.ChannelProcessingFilter"&gt;
         <para><programlisting>&lt;bean id="channelProcessingFilter" class="net.sf.acegisecurity.securechannel.ChannelProcessingFilter"&gt;
   &lt;property name="channelDecisionManager"&gt;&lt;ref bean="channelDecisionManager"/&gt;&lt;/property&gt;
   &lt;property name="channelDecisionManager"&gt;&lt;ref bean="channelDecisionManager"/&gt;&lt;/property&gt;
-  &lt;property name="secureChannelEntryPoint"&gt;&lt;ref bean="secureChannelEntryPoint"/&gt;&lt;/property&gt;
-  &lt;property name="insecureChannelEntryPoint"&gt;&lt;ref bean="insecureChannelEntryPoint"/&gt;&lt;/property&gt;
   &lt;property name="filterInvocationDefinitionSource"&gt;
   &lt;property name="filterInvocationDefinitionSource"&gt;
     &lt;value&gt;
     &lt;value&gt;
       CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
       CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
       \A/secure/.*\Z=REQUIRES_SECURE_CHANNEL
       \A/secure/.*\Z=REQUIRES_SECURE_CHANNEL
       \A/acegilogin.jsp.*\Z=REQUIRES_SECURE_CHANNEL
       \A/acegilogin.jsp.*\Z=REQUIRES_SECURE_CHANNEL
-      \A/j_acegi_security_check.*\Z=REQUIRES_SECURE_CHANNEL
+      \A/j_acegi_security_check.*\Z=REQUIRES_SECURE_CHANNEL	
       \A.*\Z=REQUIRES_INSECURE_CHANNEL
       \A.*\Z=REQUIRES_INSECURE_CHANNEL
     &lt;/value&gt;
     &lt;/value&gt;
   &lt;/property&gt;
   &lt;/property&gt;
 &lt;/bean&gt;
 &lt;/bean&gt;
 
 
-&lt;bean id="channelDecisionManager" class="net.sf.acegisecurity.securechannel.ChannelDecisionManagerImpl"/&gt;
-
-&lt;bean id="secureChannelEntryPoint" class="net.sf.acegisecurity.securechannel.RetryWithHttpsEntryPoint"&gt;
-  &lt;property name="portMapper"&gt;&lt;ref bean="portMapper"/&gt;&lt;/property&gt;
-  &lt;property name="portResolver"&gt;&lt;ref bean="portResolver"/&gt;&lt;/property&gt;
+&lt;bean id="channelDecisionManager" class="net.sf.acegisecurity.securechannel.ChannelDecisionManagerImpl"&gt;
+  &lt;property name="channelProcessors"&gt;
+    &lt;list&gt;
+      &lt;ref bean="secureChannelProcessor"/&gt;
+      &lt;ref bean="insecureChannelProcessor"/&gt;
+    &lt;/list&gt;
+  &lt;/property&gt;
 &lt;/bean&gt;
 &lt;/bean&gt;
 
 
-&lt;bean id="insecureChannelEntryPoint" class="net.sf.acegisecurity.securechannel.RetryWithHttpEntryPoint"&gt;
-  &lt;property name="portMapper"&gt;&lt;ref bean="portMapper"/&gt;&lt;/property&gt;
-  &lt;property name="portResolver"&gt;&lt;ref bean="portResolver"/&gt;&lt;/property&gt;
-&lt;/bean&gt;</programlisting></para>
+&lt;bean id="secureChannelProcessor" class="net.sf.acegisecurity.securechannel.SecureChannelProcessor"/&gt;
+&lt;bean id="insecureChannelProcessor" class="net.sf.acegisecurity.securechannel.InsecureChannelProcessor"/&gt;</programlisting></para>
 
 
         <para>Like <literal>FilterSecurityInterceptor</literal>, Apache Ant
         <para>Like <literal>FilterSecurityInterceptor</literal>, Apache Ant
         style paths are also supported by the
         style paths are also supported by the
@@ -2843,36 +2827,41 @@ $CATALINA_HOME/bin/startup.sh</programlisting></para>
         attributes that apply. It then delegates to the
         attributes that apply. It then delegates to the
         <literal>ChannelDecisionManager</literal>. The default implementation,
         <literal>ChannelDecisionManager</literal>. The default implementation,
         <literal>ChannelDecisionManagerImpl</literal>, should suffice in most
         <literal>ChannelDecisionManagerImpl</literal>, should suffice in most
-        cases. It simply throws a
-        <literal>SecureChannelRequiredException</literal> or
-        <literal>InsecureChannelRequiredException</literal> if the request's
-        transport channel carries too little or too much security
-        respectively. </para>
-
-        <para>The <literal>ChannelProcessingFilter</literal> will detect the
-        <literal>SecureChannelRequiredException</literal> or
-        <literal>InsecureChannelRequiredException</literal> and delegate to
-        the <literal>secureChannelEntryPoint</literal> or
-        <literal>insecureChannelEntryPoint</literal> respectively. These entry
-        points implement the <literal>ChannelEntryPoint</literal> interface,
-        which allows the implementation to perform a redirect or take similar
-        action. The included <literal>RetryWithHttpsEntryPoint</literal> and
-        <literal>RetryWithHttpEntryPoint</literal> implementations simply
-        perform a redirect.</para>
+        cases. It simply delegates through the list of configured
+        <literal>ChannelProcessor</literal> instances. A
+        <literal>ChannelProcessor</literal> will review the request, and if it
+        is unhappy with the request (eg it was received across the incorrect
+        transport protocol), it will perform a redirect, throw an exception or
+        take whatever other action is appropriate.</para>
+
+        <para>Included with the Acegi Security System for Spring are two
+        concrete <literal>ChannelProcessor</literal> implementations:
+        <literal>SecureChannelProcessor</literal> ensures requests with a
+        configuration attribute of <literal>REQUIRES_SECURE_CHANNEL</literal>
+        are received over HTTPS, whilst
+        <literal>InsecureChannelProcessor</literal> ensures requests with a
+        configuration attribute of
+        <literal>REQUIRES_INSECURE_CHANNEL</literal> are received over HTTP.
+        Both implementations delegate to a
+        <literal>ChannelEntryPoint</literal> if the required transport
+        protocol is not used. The two <literal>ChannelEntryPoint</literal>
+        implementations included with Acegi Security simply redirect the
+        request to HTTP and HTTPS as appropriate. Appropriate defaults are
+        assigned to the <literal>ChannelProcessor</literal> implementations
+        for the configuration attribute keywords they respond to and the
+        <literal>ChannelEntryPoint</literal> they delegate to, although you
+        have the ability to override these using the application
+        context.</para>
 
 
         <para>Note that the redirections are absolute (eg
         <para>Note that the redirections are absolute (eg
         http://www.company.com:8080/app/page), not relative (eg /app/page).
         http://www.company.com:8080/app/page), not relative (eg /app/page).
         During testing it was discovered that Internet Explorer 6 Service Pack
         During testing it was discovered that Internet Explorer 6 Service Pack
-        1 appears to have a bug whereby it does not respond correctly to a
-        redirection instruction which also changes the port to use.
-        Accordingly, absolute URLs are used in conjunction with the
-        <literal>PortResolver</literal> interface to overcome this issue. The
-        <literal>PortResolverImpl</literal> is the included implementation,
-        and is capable of determining the port a request was received on
-        either from the <literal>ServletRequest.getServerPort()</literal>
-        method or from properties defined in the application context. Please
-        refer to the JavaDocs for <literal>PortResolverImpl</literal> for
-        further details.</para>
+        1 has a bug whereby it does not respond correctly to a redirection
+        instruction which also changes the port to use. Accordingly, absolute
+        URLs are used in conjunction with bug detection logic in the
+        <literal>PortResolverImpl</literal> that is wired up by default to
+        many Acegi Security beans. Please refer to the JavaDocs for
+        <literal>PortResolverImpl</literal> for further details.</para>
       </sect2>
       </sect2>
 
 
       <sect2 id="security-channels-usage">
       <sect2 id="security-channels-usage">
@@ -2885,6 +2874,29 @@ $CATALINA_HOME/bin/startup.sh</programlisting></para>
         <literal>web.xml</literal> <literal>&lt;welcome-file&gt;</literal> or
         <literal>web.xml</literal> <literal>&lt;welcome-file&gt;</literal> or
         a well-known home page URL), but once this is done the filter will
         a well-known home page URL), but once this is done the filter will
         perform redirects as defined by your application context.</para>
         perform redirects as defined by your application context.</para>
+
+        <para>You can also add your own <literal>ChannelProcessor</literal>
+        implementations to the <literal>ChannelDecisionManagerImpl</literal>.
+        For example, you might set a <literal>HttpSession</literal> attribute
+        when a human user is detected via a "enter the contents of this
+        graphic" procedure. Your <literal>ChannelProcessor</literal> would
+        respond to say <literal>REQUIRES_HUMAN_USER</literal> configuration
+        attributes and redirect to an appropriate entry point to start the
+        human user validation process if the <literal>HttpSession</literal>
+        attribute is not currently set. </para>
+
+        <para>To decide whether a security check belongs in a
+        <literal>ChannelProcessor</literal> or an
+        <literal>AccessDecisionVoter</literal>, remember that the former is
+        designed to handle unauthenticated requests, whilst the latter is
+        designed to handle authenticated requests. The latter therefore has
+        access to the granted authorities of the authenticated principal. In
+        addition, problems detected by a <literal>ChannelProcessor</literal>
+        will generally cause a HTTP/HTTPS redirection so its requirements can
+        be met, whilst problems detected by an
+        <literal>AccessDecisionVoter</literal> will ultimately result in an
+        <literal>AccessDeniedException</literal> (depending on the governing
+        <literal>AccessDecisionManager</literal>).</para>
       </sect2>
       </sect2>
     </sect1>
     </sect1>
 
 

+ 9 - 20
samples/contacts/etc/cas/applicationContext.xml

@@ -163,8 +163,6 @@
 	
 	
 	<bean id="channelProcessingFilter" class="net.sf.acegisecurity.securechannel.ChannelProcessingFilter">
 	<bean id="channelProcessingFilter" class="net.sf.acegisecurity.securechannel.ChannelProcessingFilter">
 		<property name="channelDecisionManager"><ref bean="channelDecisionManager"/></property>
 		<property name="channelDecisionManager"><ref bean="channelDecisionManager"/></property>
-		<property name="secureChannelEntryPoint"><ref bean="secureChannelEntryPoint"/></property>
-		<property name="insecureChannelEntryPoint"><ref bean="insecureChannelEntryPoint"/></property>
  		<property name="filterInvocationDefinitionSource">
  		<property name="filterInvocationDefinitionSource">
 			<value>
 			<value>
 			    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
 			    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
@@ -175,17 +173,17 @@
 		</property>
 		</property>
 	</bean>
 	</bean>
 
 
-	<bean id="channelDecisionManager" class="net.sf.acegisecurity.securechannel.ChannelDecisionManagerImpl"/>
-
-	<bean id="secureChannelEntryPoint" class="net.sf.acegisecurity.securechannel.RetryWithHttpsEntryPoint">
-		<property name="portMapper"><ref bean="portMapper"/></property>
-		<property name="portResolver"><ref bean="portResolver"/></property>
+	<bean id="channelDecisionManager" class="net.sf.acegisecurity.securechannel.ChannelDecisionManagerImpl">
+	    <property name="channelProcessors">
+      		<list>
+ 	        	<ref bean="secureChannelProcessor"/>
+        		<ref bean="insecureChannelProcessor"/>
+     		</list>
+	    </property>
 	</bean>
 	</bean>
 
 
-	<bean id="insecureChannelEntryPoint" class="net.sf.acegisecurity.securechannel.RetryWithHttpEntryPoint">
-		<property name="portMapper"><ref bean="portMapper"/></property>
-		<property name="portResolver"><ref bean="portResolver"/></property>
-	</bean>
+	<bean id="secureChannelProcessor" class="net.sf.acegisecurity.securechannel.SecureChannelProcessor"/>
+	<bean id="insecureChannelProcessor" class="net.sf.acegisecurity.securechannel.InsecureChannelProcessor"/>
 
 
 	<!-- ===================== HTTP REQUEST SECURITY ==================== -->
 	<!-- ===================== HTTP REQUEST SECURITY ==================== -->
 
 
@@ -199,7 +197,6 @@
 	<bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter">
 	<bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter">
 		<property name="filterSecurityInterceptor"><ref bean="filterInvocationInterceptor"/></property>
 		<property name="filterSecurityInterceptor"><ref bean="filterInvocationInterceptor"/></property>
 		<property name="authenticationEntryPoint"><ref bean="casProcessingFilterEntryPoint"/></property>
 		<property name="authenticationEntryPoint"><ref bean="casProcessingFilterEntryPoint"/></property>
-		<property name="portResolver"><ref bean="portResolver"/></property>
 	</bean>
 	</bean>
 
 
 	<bean id="casProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.cas.CasProcessingFilterEntryPoint">
 	<bean id="casProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.cas.CasProcessingFilterEntryPoint">
@@ -207,14 +204,6 @@
 		<property name="serviceProperties"><ref bean="serviceProperties"/></property>
 		<property name="serviceProperties"><ref bean="serviceProperties"/></property>
 	</bean>
 	</bean>
 
 
-	<bean id="portMapper" class="net.sf.acegisecurity.util.PortMapperImpl"/>
-	
-	<!-- Comment the always[Scheme]Port properties to use ServletRequest.getServerPort() -->
-	<bean id="portResolver" class="net.sf.acegisecurity.util.PortResolverImpl">
-		<property name="alwaysHttpPort"><value>8080</value></property>
-		<property name="alwaysHttpsPort"><value>8443</value></property>
-	</bean>
-
 	<bean id="httpRequestAccessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
 	<bean id="httpRequestAccessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
    		<property name="allowIfAllAbstainDecisions"><value>false</value></property>
    		<property name="allowIfAllAbstainDecisions"><value>false</value></property>
 		<property name="decisionVoters">
 		<property name="decisionVoters">

+ 9 - 22
samples/contacts/etc/filter/applicationContext.xml

@@ -140,8 +140,6 @@
 
 
 	<bean id="channelProcessingFilter" class="net.sf.acegisecurity.securechannel.ChannelProcessingFilter">
 	<bean id="channelProcessingFilter" class="net.sf.acegisecurity.securechannel.ChannelProcessingFilter">
 		<property name="channelDecisionManager"><ref bean="channelDecisionManager"/></property>
 		<property name="channelDecisionManager"><ref bean="channelDecisionManager"/></property>
-		<property name="secureChannelEntryPoint"><ref bean="secureChannelEntryPoint"/></property>
-		<property name="insecureChannelEntryPoint"><ref bean="insecureChannelEntryPoint"/></property>
  		<property name="filterInvocationDefinitionSource">
  		<property name="filterInvocationDefinitionSource">
 			<value>
 			<value>
 			    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
 			    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
@@ -153,17 +151,17 @@
 		</property>
 		</property>
 	</bean>
 	</bean>
 
 
-	<bean id="channelDecisionManager" class="net.sf.acegisecurity.securechannel.ChannelDecisionManagerImpl"/>
-
-	<bean id="secureChannelEntryPoint" class="net.sf.acegisecurity.securechannel.RetryWithHttpsEntryPoint">
-		<property name="portMapper"><ref bean="portMapper"/></property>
-		<property name="portResolver"><ref bean="portResolver"/></property>
+	<bean id="channelDecisionManager" class="net.sf.acegisecurity.securechannel.ChannelDecisionManagerImpl">
+	    <property name="channelProcessors">
+      		<list>
+ 	        	<ref bean="secureChannelProcessor"/>
+        		<ref bean="insecureChannelProcessor"/>
+     		</list>
+	    </property>
 	</bean>
 	</bean>
 
 
-	<bean id="insecureChannelEntryPoint" class="net.sf.acegisecurity.securechannel.RetryWithHttpEntryPoint">
-		<property name="portMapper"><ref bean="portMapper"/></property>
-		<property name="portResolver"><ref bean="portResolver"/></property>
-	</bean>
+	<bean id="secureChannelProcessor" class="net.sf.acegisecurity.securechannel.SecureChannelProcessor"/>
+	<bean id="insecureChannelProcessor" class="net.sf.acegisecurity.securechannel.InsecureChannelProcessor"/>
 
 
 	<!-- ===================== HTTP REQUEST SECURITY ==================== -->
 	<!-- ===================== HTTP REQUEST SECURITY ==================== -->
 
 
@@ -177,22 +175,11 @@
 	<bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter">
 	<bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter">
 		<property name="filterSecurityInterceptor"><ref bean="filterInvocationInterceptor"/></property>
 		<property name="filterSecurityInterceptor"><ref bean="filterInvocationInterceptor"/></property>
 		<property name="authenticationEntryPoint"><ref bean="authenticationProcessingFilterEntryPoint"/></property>
 		<property name="authenticationEntryPoint"><ref bean="authenticationProcessingFilterEntryPoint"/></property>
-		<property name="portResolver"><ref bean="portResolver"/></property>
 	</bean>
 	</bean>
 
 
 	<bean id="authenticationProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
 	<bean id="authenticationProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
 		<property name="loginFormUrl"><value>/acegilogin.jsp</value></property>
 		<property name="loginFormUrl"><value>/acegilogin.jsp</value></property>
 		<property name="forceHttps"><value>false</value></property>
 		<property name="forceHttps"><value>false</value></property>
-		<property name="portMapper"><ref bean="portMapper"/></property>
-		<property name="portResolver"><ref bean="portResolver"/></property>
-	</bean>
-
-	<bean id="portMapper" class="net.sf.acegisecurity.util.PortMapperImpl"/>
-	
-	<!-- Comment the always[Scheme]Port properties to use ServletRequest.getServerPort() -->
-	<bean id="portResolver" class="net.sf.acegisecurity.util.PortResolverImpl">
-		<property name="alwaysHttpPort"><value>8080</value></property>
-		<property name="alwaysHttpsPort"><value>8443</value></property>
 	</bean>
 	</bean>
 
 
 	<bean id="httpRequestAccessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
 	<bean id="httpRequestAccessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">

+ 2 - 2
samples/contacts/etc/filter/web.xml

@@ -67,12 +67,12 @@
 	
 	
 	<!-- Remove the comments from the following <filter-mapping> if you'd
 	<!-- Remove the comments from the following <filter-mapping> if you'd
 	     like to ensure secure URLs are only available over HTTPS -->
 	     like to ensure secure URLs are only available over HTTPS -->
-    <!--
+ 
     <filter-mapping>
     <filter-mapping>
       <filter-name>Acegi Channel Processing Filter</filter-name>
       <filter-name>Acegi Channel Processing Filter</filter-name>
       <url-pattern>/*</url-pattern>
       <url-pattern>/*</url-pattern>
     </filter-mapping>
     </filter-mapping>
-	-->
+
 	
 	
     <filter-mapping>
     <filter-mapping>
       <filter-name>Acegi Authentication Processing Filter</filter-name>
       <filter-name>Acegi Authentication Processing Filter</filter-name>