diff --git a/core/src/main/java/org/acegisecurity/intercept/web/WebInvocationPrivilegeEvaluator.java b/core/src/main/java/org/acegisecurity/intercept/web/WebInvocationPrivilegeEvaluator.java new file mode 100644 index 0000000000..f5fea4f24d --- /dev/null +++ b/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; + } +} diff --git a/core/src/main/java/org/acegisecurity/util/FilterInvocationUtils.java b/core/src/main/java/org/acegisecurity/util/FilterInvocationUtils.java new file mode 100644 index 0000000000..eed8ad8b6d --- /dev/null +++ b/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 FilterInvocations usable + * within Acegi Security. + * + *

+ * The generated FilterInvocation objects are not intended for use + * with AbstractSecurityInterceptor subclasses. Instead they are + * generally used by WebInvocationPrivilegeEvaluator. + *

+ * + * @author Ben Alex + * @version $Id$ + */ +public class FilterInvocationUtils { + //~ Methods ================================================================ + + /** + * Creates a FilterInvocation for the specified + * contextPath and Uri. Note the normal + * subclasses of AbstractFilterInvocationDefinitionSource + * disregard the contextPath when evaluating which secure + * object metadata applies to a given FilterInvocation, so + * generally the contextPath is unimportant unless you are + * using a custom FilterInvocationDefinitionSource. + * + * @param contextPath the contextPath that will be contained + * within the + * FilterInvocationHttpServletRequest + * @param uri the URI of the request, such as /foo/default.jsp + * + * @return a fully-formed FilterInvocation (never + * null) + * + * @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 FilterInvocation for the specified + * Uri. The contextPath is set to a default + * value. + * + * @param uri the URI of the request, such as /foo/default.jsp + * + * @return a fully-formed FilterInvocation (never + * null) + */ + public static FilterInvocation create(String uri) { + return create("/notused", uri); + } +} diff --git a/core/src/test/java/org/acegisecurity/intercept/web/WebInvocationPrivilegeEvaluatorTests.java b/core/src/test/java/org/acegisecurity/intercept/web/WebInvocationPrivilegeEvaluatorTests.java new file mode 100644 index 0000000000..07b99a7465 --- /dev/null +++ b/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)); + } +} diff --git a/core/src/test/resources/org/acegisecurity/intercept/web/applicationContext.xml b/core/src/test/resources/org/acegisecurity/intercept/web/applicationContext.xml new file mode 100644 index 0000000000..e40ed74516 --- /dev/null +++ b/core/src/test/resources/org/acegisecurity/intercept/web/applicationContext.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON + PATTERN_TYPE_APACHE_ANT + /foo/index.jsp=MOCK_INDEX + /hello.htm=MOCK_HELLO + /**=MOCK_USER + + + + +