Ver Fonte

SEC-1149: WebInvocationPrivilegeEvaluator now contains methods to evaluate the permissions on URIs directly. Deleted FilterInvocationUtils.

Luke Taylor há 16 anos atrás
pai
commit
dca566ff1f

+ 1 - 1
web/src/main/java/org/springframework/security/web/intercept/DefaultFilterInvocationSecurityMetadataSource.java

@@ -158,7 +158,7 @@ public class DefaultFilterInvocationSecurityMetadataSource implements FilterInvo
      * Subclasses can override if required to perform any modifications to the URL.
      *
      * @param url the URI to retrieve configuration attributes for
-     * @param method the HTTP method (GET, POST, DELETE...).
+     * @param method the HTTP method (GET, POST, DELETE...), or null for any method.
      *
      * @return the <code>ConfigAttribute</code>s that apply to the specified <code>FilterInvocation</code>
      * or null if no match is found

+ 70 - 14
web/src/main/java/org/springframework/security/web/intercept/WebInvocationPrivilegeEvaluator.java

@@ -15,11 +15,18 @@
 
 package org.springframework.security.web.intercept;
 
+import java.io.IOException;
 import java.util.List;
 
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.springframework.beans.factory.InitializingBean;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
 import org.springframework.security.access.AccessDeniedException;
 import org.springframework.security.access.ConfigAttribute;
 import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
@@ -34,24 +41,69 @@ import org.springframework.util.Assert;
  * @author Ben Alex
  * @version $Id$
  */
-public class WebInvocationPrivilegeEvaluator implements InitializingBean {
+public class WebInvocationPrivilegeEvaluator {
     //~ Static fields/initializers =====================================================================================
 
     protected static final Log logger = LogFactory.getLog(WebInvocationPrivilegeEvaluator.class);
 
+    static final FilterChain DUMMY_CHAIN = new FilterChain() {
+        public void doFilter(ServletRequest req, ServletResponse res) throws IOException, ServletException {
+            throw new UnsupportedOperationException("WebInvocationPrivilegeEvaluator does not support filter chains");
+        }
+    };
+
     //~ Instance fields ================================================================================================
 
     private AbstractSecurityInterceptor securityInterceptor;
 
+    //~ Constructors ===================================================================================================
+
+    public WebInvocationPrivilegeEvaluator(AbstractSecurityInterceptor securityInterceptor) {
+        Assert.notNull(securityInterceptor, "SecurityInterceptor cannot be null");
+        Assert.isTrue(FilterInvocation.class.equals(securityInterceptor.getSecureObjectClass()),
+            "AbstractSecurityInterceptor does not support FilterInvocations");
+        Assert.notNull(securityInterceptor.getAccessDecisionManager(),
+            "AbstractSecurityInterceptor must provide a non-null AccessDecisionManager");
+
+        this.securityInterceptor = securityInterceptor;
+    }
+
     //~ Methods ========================================================================================================
 
-    public void afterPropertiesSet() throws Exception {
-        Assert.notNull(securityInterceptor, "SecurityInterceptor required");
+    /**
+     * Determines whether the user represented by the supplied <tt>Authentication</tt> object is
+     * allowed to invoke the supplied URI.
+     *
+     * @param uri the URI excluding the context path (a default context path setting will be used)
+     */
+    public boolean isAllowed(String uri, Authentication authentication) {
+        return isAllowed(null, uri, null, authentication);
     }
 
-    public boolean isAllowed(FilterInvocation fi, Authentication authentication) {
-        Assert.notNull(fi, "FilterInvocation required");
+    /**
+     * Determines whether the user represented by the supplied <tt>Authentication</tt> object is
+     * allowed to invoke the supplied URI, with the given .
+     * <p>
+     * Note the default implementation of <tt>FilterInvocationSecurityMetadataSource</tt> disregards the
+     * <code>contextPath</code> when evaluating which secure object metadata applies to a given
+     * request URI, so generally the <code>contextPath</code> is unimportant unless you
+     * are using a custom <code>FilterInvocationSecurityMetadataSource</code>.
+     *
+     * @param uri the URI excluding the context path
+     * @param contextPath the context path (may be null, in which case a default value will be used).
+     * @param method the HTTP method (or null, for any method)
+     * @param authentication the <tt>Authentication</tt> instance whose authorities should be used in evaluation
+     *          whether access should be granted.
+     * @return true if access is allowed, false if denied
+     */
+    public boolean isAllowed(String contextPath, String uri, String method, Authentication authentication) {
+        Assert.notNull(uri, "uri parameter is required");
+
+        if (contextPath == null) {
+            contextPath = "/ctxpath";
+        }
 
+        FilterInvocation fi = createFilterInvocation(contextPath, uri, method);
         List<ConfigAttribute> attrs = securityInterceptor.obtainSecurityMetadataSource().getAttributes(fi);
 
         if (attrs == null) {
@@ -63,7 +115,7 @@ public class WebInvocationPrivilegeEvaluator implements InitializingBean {
         }
 
         if ((authentication == null) || (authentication.getAuthorities() == null)
-            || authentication.getAuthorities().isEmpty()) {
+                || authentication.getAuthorities().isEmpty()) {
             return false;
         }
 
@@ -80,12 +132,16 @@ public class WebInvocationPrivilegeEvaluator implements InitializingBean {
         return true;
     }
 
-    public void setSecurityInterceptor(AbstractSecurityInterceptor securityInterceptor) {
-        Assert.notNull(securityInterceptor, "AbstractSecurityInterceptor cannot be null");
-        Assert.isTrue(FilterInvocation.class.equals(securityInterceptor.getSecureObjectClass()),
-            "AbstractSecurityInterceptor does not support FilterInvocations");
-        Assert.notNull(securityInterceptor.getAccessDecisionManager(),
-            "AbstractSecurityInterceptor must provide a non-null AccessDecisionManager");
-        this.securityInterceptor = securityInterceptor;
+    private FilterInvocation createFilterInvocation(String contextPath, String uri, String method) {
+        Assert.hasText(contextPath, "contextPath required");
+        Assert.hasText(uri, "URI required");
+
+        MockHttpServletRequest req = new MockHttpServletRequest();
+        req.setRequestURI(contextPath + uri);
+        req.setContextPath(contextPath);
+        req.setServletPath(null);
+        req.setMethod(method);
+
+        return new FilterInvocation(req, new MockHttpServletResponse(), DUMMY_CHAIN);
     }
 }

+ 0 - 91
web/src/main/java/org/springframework/security/web/util/FilterInvocationUtils.java

@@ -1,91 +0,0 @@
-/* Copyright 2004, 2005, 2006 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 org.springframework.security.web.util;
-
-import java.io.IOException;
-
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.mock.web.MockHttpServletResponse;
-import org.springframework.security.web.FilterInvocation;
-import org.springframework.util.Assert;
-
-
-/**
- * Static utility methods for creating <code>FilterInvocation</code>s usable within Spring Security.<p>The generated
- * <code>FilterInvocation</code> objects are not intended for use with <code>AbstractSecurityInterceptor</code>
- * subclasses. Instead they are generally used by <code>WebInvocationPrivilegeEvaluator</code>.</p>
- *
- * @author Ben Alex
- * @version $Id$
- */
-public final class FilterInvocationUtils {
-    //~ Constructors ===================================================================================================
-
-    private FilterInvocationUtils() {
-    }
-
-    //~ Methods ========================================================================================================
-
-    /**
-     * Creates a <code>FilterInvocation</code> for the specified <code>contextPath</code> and <code>Uri</code>.
-     * Note the normal subclasses of <tt>DefaultFilterInvocationSecurityMetadataSource</tt> disregard the
-     * <code>contextPath</code> when evaluating which secure object metadata applies to a given
-     * <code>FilterInvocation</code>, so generally the <code>contextPath</code> is unimportant unless you are using a
-     * custom <code>FilterInvocationSecurityMetadataSource</code>.
-     *
-     * @param contextPath the <code>contextPath</code> that will be contained within the
-     *        <code>FilterInvocation</code><code>HttpServletRequest</code>
-     * @param uri the URI of the request, such as <code>/foo/default.jsp</code>
-     *
-     * @return a fully-formed <code>FilterInvocation</code> (never <code>null</code>)
-     */
-    public static FilterInvocation create(String contextPath, String uri) {
-        Assert.hasText(contextPath, "contextPath required");
-        Assert.hasText(uri, "URI required");
-
-        MockHttpServletRequest req = new MockHttpServletRequest();
-        req.setRequestURI(contextPath + uri);
-        req.setContextPath(contextPath);
-        req.setServletPath(null);
-
-        FilterInvocation fi = new FilterInvocation(req, new MockHttpServletResponse(),
-                new FilterChain() {
-                    public void doFilter(ServletRequest arg0, ServletResponse arg1) throws IOException, ServletException {
-                        throw new UnsupportedOperationException(
-                            "WebInvocationPrivilegeEvaluator does not support filter chains");
-                    }
-                });
-
-        return fi;
-    }
-
-    /**
-     * Creates a <code>FilterInvocation</code> for the specified <code>Uri</code>. The <code>contextPath</code>
-     * is set to a default value.
-     *
-     * @param uri the URI of the request, such as <code>/foo/default.jsp</code>
-     *
-     * @return a fully-formed <code>FilterInvocation</code> (never <code>null</code>)
-     */
-    public static FilterInvocation create(String uri) {
-        return create("/notused", uri);
-    }
-}

+ 38 - 49
web/src/test/java/org/springframework/security/web/intercept/WebInvocationPrivilegeEvaluatorTests.java

@@ -15,16 +15,13 @@
 
 package org.springframework.security.web.intercept;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.springframework.security.matcher.AuthenticationMatcher.anAuthenticationWithUsername;
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
 
-import java.util.List;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
-import org.jmock.Expectations;
-import org.jmock.Mockery;
-import org.jmock.integration.junit4.JUnit4Mockery;
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.springframework.security.MockApplicationEventPublisher;
@@ -35,11 +32,6 @@ import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.web.FilterInvocation;
-import org.springframework.security.web.intercept.FilterInvocationSecurityMetadataSource;
-import org.springframework.security.web.intercept.FilterSecurityInterceptor;
-import org.springframework.security.web.intercept.WebInvocationPrivilegeEvaluator;
-import org.springframework.security.web.util.FilterInvocationUtils;
 
 
 /**
@@ -49,8 +41,6 @@ import org.springframework.security.web.util.FilterInvocationUtils;
  * @version $Id$
  */
 public class WebInvocationPrivilegeEvaluatorTests {
-    private Mockery jmock = new JUnit4Mockery();
-    private AuthenticationManager am;
     private AccessDecisionManager adm;
     private FilterInvocationSecurityMetadataSource ods;
     private RunAsManager ram;
@@ -59,13 +49,12 @@ public class WebInvocationPrivilegeEvaluatorTests {
     //~ Methods ========================================================================================================
 
     @Before
-    public final void setUp() throws Exception {
+    public final void setUp() {
         interceptor = new FilterSecurityInterceptor();
-        am = jmock.mock(AuthenticationManager.class);
-        ods = jmock.mock(FilterInvocationSecurityMetadataSource.class);
-        adm = jmock.mock(AccessDecisionManager.class);
-        ram = jmock.mock(RunAsManager.class);
-        interceptor.setAuthenticationManager(am);
+        ods = mock(FilterInvocationSecurityMetadataSource.class);
+        adm = mock(AccessDecisionManager.class);
+        ram = mock(RunAsManager.class);
+        interceptor.setAuthenticationManager(mock(AuthenticationManager.class));
         interceptor.setSecurityMetadataSource(ods);
         interceptor.setAccessDecisionManager(adm);
         interceptor.setRunAsManager(ram);
@@ -73,47 +62,47 @@ public class WebInvocationPrivilegeEvaluatorTests {
         SecurityContextHolder.clearContext();
     }
 
-    @After
-    public void tearDown() throws Exception {
-        SecurityContextHolder.clearContext();
+    @Test
+    public void permitsAccessIfNoMatchingAttributesAndPublicInvocationsAllowed() throws Exception {
+        WebInvocationPrivilegeEvaluator wipe = new WebInvocationPrivilegeEvaluator(interceptor);
+        when(ods.getAttributes(anyObject())).thenReturn(null);
+        assertTrue(wipe.isAllowed("/context", "/foo/index.jsp", "GET", mock(Authentication.class)));
     }
 
-    @SuppressWarnings("unchecked")
     @Test
-    public void allowsAccessIfAccessDecisionMangerDoes() throws Exception {
-        Authentication token = new TestingAuthenticationToken("test", "Password", "MOCK_INDEX");
-        FilterInvocation fi = FilterInvocationUtils.create("/foo/index.jsp");
-
-        WebInvocationPrivilegeEvaluator wipe = new WebInvocationPrivilegeEvaluator();
-        wipe.setSecurityInterceptor(interceptor);
-        wipe.afterPropertiesSet();
+    public void deniesAccessIfNoMatchingAttributesAndPublicInvocationsNotAllowed() throws Exception {
+        WebInvocationPrivilegeEvaluator wipe = new WebInvocationPrivilegeEvaluator(interceptor);
+        when(ods.getAttributes(anyObject())).thenReturn(null);
+        interceptor.setRejectPublicInvocations(true);
+        assertFalse(wipe.isAllowed("/context", "/foo/index.jsp", "GET", mock(Authentication.class)));
+    }
 
-        jmock.checking(new Expectations() {{
-            ignoring(ram); ignoring(ods);
-            oneOf(adm).decide(with(anAuthenticationWithUsername("test")), with(anything()), with(aNonNull(List.class)));
-        }});
+    @Test
+    public void deniesAccessIfAuthenticationIsNull() throws Exception {
+        WebInvocationPrivilegeEvaluator wipe = new WebInvocationPrivilegeEvaluator(interceptor);
+        assertFalse(wipe.isAllowed("/foo/index.jsp", null));
+    }
 
-        assertTrue(wipe.isAllowed(fi, token));
-        jmock.assertIsSatisfied();
+    @Test
+    public void allowsAccessIfAccessDecisionMangerDoes() throws Exception {
+        Authentication token = new TestingAuthenticationToken("test", "Password", "MOCK_INDEX");
+        WebInvocationPrivilegeEvaluator wipe = new WebInvocationPrivilegeEvaluator(interceptor);
+        assertTrue(wipe.isAllowed("/foo/index.jsp", token));
     }
 
     @SuppressWarnings("unchecked")
     @Test
     public void deniesAccessIfAccessDecisionMangerDoes() throws Exception {
         Authentication token = new TestingAuthenticationToken("test", "Password", "MOCK_INDEX");
-        FilterInvocation fi = FilterInvocationUtils.create("/foo/index.jsp");
+        WebInvocationPrivilegeEvaluator wipe = new WebInvocationPrivilegeEvaluator(interceptor);
 
-        WebInvocationPrivilegeEvaluator wipe = new WebInvocationPrivilegeEvaluator();
-        wipe.setSecurityInterceptor(interceptor);
-        wipe.afterPropertiesSet();
+        doThrow(new AccessDeniedException("")).when(adm).decide(any(Authentication.class), anyObject(), anyList());
 
-        jmock.checking(new Expectations() {{
-            ignoring(ram); ignoring(ods);
-            oneOf(adm).decide(with(anAuthenticationWithUsername("test")), with(anything()), with(aNonNull(List.class)));
-                will(throwException(new AccessDeniedException("")));
-        }});
+        assertFalse(wipe.isAllowed("/foo/index.jsp", token));
+    }
 
-        assertFalse(wipe.isAllowed(fi, token));
-        jmock.assertIsSatisfied();
+    @Test(expected=UnsupportedOperationException.class)
+    public void dummyChainRejectsInvocation() throws Exception {
+        WebInvocationPrivilegeEvaluator.DUMMY_CHAIN.doFilter(mock(HttpServletRequest.class), mock(HttpServletResponse.class));
     }
 }