浏览代码

SEC-126: Initial commit of WebInvocationPrivilegeEvaluator feature.

Ben Alex 19 年之前
父节点
当前提交
484b0e3a51

+ 99 - 0
core/src/main/java/org/acegisecurity/intercept/web/WebInvocationPrivilegeEvaluator.java

@@ -0,0 +1,99 @@
+/* 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.acegisecurity.intercept.web;
+
+import org.acegisecurity.AccessDeniedException;
+import org.acegisecurity.Authentication;
+import org.acegisecurity.ConfigAttributeDefinition;
+
+import org.acegisecurity.intercept.AbstractSecurityInterceptor;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.factory.InitializingBean;
+
+import org.springframework.util.Assert;
+
+
+/**
+ * Allows users to determine whether they have privileges for a given web URI.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class WebInvocationPrivilegeEvaluator implements InitializingBean {
+    //~ Static fields/initializers =============================================
+
+    protected static final Log logger = LogFactory.getLog(WebInvocationPrivilegeEvaluator.class);
+
+    //~ Instance fields ========================================================
+
+    private AbstractSecurityInterceptor securityInterceptor;
+
+    //~ Methods ================================================================
+
+    public void afterPropertiesSet() throws Exception {
+        Assert.notNull(securityInterceptor, "SecurityInterceptor required");
+    }
+
+    public boolean isAllowed(FilterInvocation fi, Authentication authentication) {
+        Assert.notNull(fi, "FilterInvocation required");
+
+        ConfigAttributeDefinition attrs = securityInterceptor.obtainObjectDefinitionSource()
+                                                             .getAttributes(fi);
+
+        if (attrs == null) {
+            if (securityInterceptor.isRejectPublicInvocations()) {
+                return false;
+            }
+
+            return true;
+        }
+
+        if ((authentication == null)
+            || (authentication.getAuthorities() == null)
+            || (authentication.getAuthorities().length == 0)) {
+            return false;
+        }
+
+        try {
+            securityInterceptor.getAccessDecisionManager()
+                               .decide(authentication, fi, attrs);
+        } catch (AccessDeniedException unauthorized) {
+            if (logger.isDebugEnabled()) {
+                logger.debug(fi.toString() + " denied for "
+                    + authentication.toString(), unauthorized);
+            }
+
+            return false;
+        }
+
+        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;
+    }
+}

+ 104 - 0
core/src/main/java/org/acegisecurity/util/FilterInvocationUtils.java

@@ -0,0 +1,104 @@
+/* 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.acegisecurity.util;
+
+import org.acegisecurity.intercept.web.FilterInvocation;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+
+import org.springframework.util.Assert;
+
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+
+/**
+ * Static utility methods for creating <code>FilterInvocation</code>s usable
+ * within Acegi 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 class FilterInvocationUtils {
+    //~ Methods ================================================================
+
+    /**
+     * Creates a <code>FilterInvocation</code> for the specified
+     * <code>contextPath</code> and <code>Uri</code>. Note the normal
+     * subclasses of <code>AbstractFilterInvocationDefinitionSource</code>
+     * 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>FilterInvocationDefinitionSource</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>)
+     *
+     * @throws UnsupportedOperationException DOCUMENT ME!
+     */
+    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);
+    }
+}

+ 104 - 0
core/src/test/java/org/acegisecurity/intercept/web/WebInvocationPrivilegeEvaluatorTests.java

@@ -0,0 +1,104 @@
+/* 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.acegisecurity.intercept.web;
+
+import junit.framework.TestCase;
+
+import org.acegisecurity.GrantedAuthority;
+import org.acegisecurity.GrantedAuthorityImpl;
+
+import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
+
+import org.acegisecurity.util.FilterInvocationUtils;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+
+/**
+ * Tests {@link
+ * org.acegisecurity.intercept.web.WebInvocationPrivilegeEvaluator}.
+ *
+ * @author Ben Alex
+ * @version $Id$
+ */
+public class WebInvocationPrivilegeEvaluatorTests extends TestCase {
+    //~ Constructors ===========================================================
+
+    public WebInvocationPrivilegeEvaluatorTests() {
+        super();
+    }
+
+    public WebInvocationPrivilegeEvaluatorTests(String arg0) {
+        super(arg0);
+    }
+
+    //~ Methods ================================================================
+
+    public static void main(String[] args) {
+        junit.textui.TestRunner.run(WebInvocationPrivilegeEvaluatorTests.class);
+    }
+
+    private FilterSecurityInterceptor makeFilterSecurityInterceptor() {
+        ApplicationContext context = new ClassPathXmlApplicationContext(
+                "org/acegisecurity/intercept/web/applicationContext.xml");
+
+        return (FilterSecurityInterceptor) context.getBean(
+            "securityInterceptor");
+    }
+
+    public void testAllowsAccess1() throws Exception {
+        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
+                "Password",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_INDEX")});
+        FilterInvocation fi = FilterInvocationUtils.create("/foo/index.jsp");
+        FilterSecurityInterceptor interceptor = makeFilterSecurityInterceptor();
+
+        WebInvocationPrivilegeEvaluator wipe = new WebInvocationPrivilegeEvaluator();
+        wipe.setSecurityInterceptor(interceptor);
+        wipe.afterPropertiesSet();
+
+        assertTrue(wipe.isAllowed(fi, token));
+    }
+
+    public void testAllowsAccess2() throws Exception {
+        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
+                "Password",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_USER")});
+        FilterInvocation fi = FilterInvocationUtils.create("/anything.jsp");
+        FilterSecurityInterceptor interceptor = makeFilterSecurityInterceptor();
+
+        WebInvocationPrivilegeEvaluator wipe = new WebInvocationPrivilegeEvaluator();
+        wipe.setSecurityInterceptor(interceptor);
+        wipe.afterPropertiesSet();
+
+        assertTrue(wipe.isAllowed(fi, token));
+    }
+
+    public void testDeniesAccess1() throws Exception {
+        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test",
+                "Password",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_NOTHING_USEFUL")});
+        FilterInvocation fi = FilterInvocationUtils.create("/anything.jsp");
+        FilterSecurityInterceptor interceptor = makeFilterSecurityInterceptor();
+
+        WebInvocationPrivilegeEvaluator wipe = new WebInvocationPrivilegeEvaluator();
+        wipe.setSecurityInterceptor(interceptor);
+        wipe.afterPropertiesSet();
+
+        assertFalse(wipe.isAllowed(fi, token));
+    }
+}

+ 41 - 0
core/src/test/resources/org/acegisecurity/intercept/web/applicationContext.xml

@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
+<!--
+ * Copyright 2004, 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.
+ *
+ * $Id$
+-->
+
+<beans>
+	<bean id="authentication" class="org.acegisecurity.MockAuthenticationManager"/>
+	<bean id="accessDecision" class="org.acegisecurity.MockAccessDecisionManager"/>
+	<bean id="runAs" class="org.acegisecurity.MockRunAsManager"/>
+
+	<bean id="securityInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
+		<property name="authenticationManager"><ref local="authentication"/></property>
+		<property name="accessDecisionManager"><ref local="accessDecision"/></property>
+		<property name="runAsManager"><ref local="runAs"/></property>
+		<property name="objectDefinitionSource">
+        	<value>
+			    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
+			    PATTERN_TYPE_APACHE_ANT
+			    /foo/index.jsp=MOCK_INDEX
+			    /hello.htm=MOCK_HELLO
+				/**=MOCK_USER
+        	</value>
+		</property>
+	</bean>
+
+</beans>