SEC-525: [PATCH] Add AccessCheckerTag based on URL resource access permissions. Added functionality to "authorize" tag to allow evaluation of whether a particual url is accessible to the user. Uses a WebInvocationPrivilegeEvaluator registered in the application context.
This commit is contained in:
parent
1c4a809e09
commit
731402e9f5
|
|
@ -40,6 +40,7 @@ import org.springframework.security.web.FilterChainProxy;
|
||||||
import org.springframework.security.web.PortResolverImpl;
|
import org.springframework.security.web.PortResolverImpl;
|
||||||
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
||||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
||||||
|
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
|
||||||
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
|
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
|
||||||
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
|
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
|
||||||
import org.springframework.security.web.access.channel.InsecureChannelProcessor;
|
import org.springframework.security.web.access.channel.InsecureChannelProcessor;
|
||||||
|
|
@ -218,7 +219,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
|
|
||||||
sessionStrategyRef = (BeanReference) (sessionStrategyPV == null ? null : sessionStrategyPV.getValue());
|
sessionStrategyRef = (BeanReference) (sessionStrategyPV == null ? null : sessionStrategyPV.getValue());
|
||||||
}
|
}
|
||||||
BeanDefinition fsi = createFilterSecurityInterceptor(element, pc, matcher, convertPathsToLowerCase, authenticationManager);
|
BeanReference fsi = createFilterSecurityInterceptor(element, pc, matcher, convertPathsToLowerCase, authenticationManager);
|
||||||
|
|
||||||
if (channelRequestMap.size() > 0) {
|
if (channelRequestMap.size() > 0) {
|
||||||
// At least one channel requirement has been specified
|
// At least one channel requirement has been specified
|
||||||
|
|
@ -791,7 +792,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
return accessDeniedHandler.getBeanDefinition();
|
return accessDeniedHandler.getBeanDefinition();
|
||||||
}
|
}
|
||||||
|
|
||||||
private BeanDefinition createFilterSecurityInterceptor(Element element, ParserContext pc, UrlMatcher matcher,
|
private BeanReference createFilterSecurityInterceptor(Element element, ParserContext pc, UrlMatcher matcher,
|
||||||
boolean convertPathsToLowerCase, BeanReference authManager) {
|
boolean convertPathsToLowerCase, BeanReference authManager) {
|
||||||
BeanDefinitionBuilder fidsBuilder;
|
BeanDefinitionBuilder fidsBuilder;
|
||||||
|
|
||||||
|
|
@ -851,7 +852,17 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.addPropertyValue("securityMetadataSource", fidsBuilder.getBeanDefinition());
|
builder.addPropertyValue("securityMetadataSource", fidsBuilder.getBeanDefinition());
|
||||||
return builder.getBeanDefinition();
|
BeanDefinition fsi = builder.getBeanDefinition();
|
||||||
|
String fsiId = pc.getReaderContext().registerWithGeneratedName(fsi);
|
||||||
|
pc.registerBeanComponent(new BeanComponentDefinition(fsi,fsiId));
|
||||||
|
|
||||||
|
// Create and register a DefaultWebInvocationPrivilegeEvaluator for use with taglibs etc.
|
||||||
|
BeanDefinition wipe = new RootBeanDefinition(DefaultWebInvocationPrivilegeEvaluator.class);
|
||||||
|
wipe.getConstructorArgumentValues().addGenericArgumentValue(new RuntimeBeanReference(fsiId));
|
||||||
|
String wipeId = pc.getReaderContext().registerWithGeneratedName(wipe);
|
||||||
|
pc.registerBeanComponent(new BeanComponentDefinition(wipe, wipeId));
|
||||||
|
|
||||||
|
return new RuntimeBeanReference(fsiId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BeanDefinition createChannelProcessingFilter(ParserContext pc, UrlMatcher matcher,
|
private BeanDefinition createChannelProcessingFilter(ParserContext pc, UrlMatcher matcher,
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,15 @@ If you're logged in, you can <a href="listAccounts.html">list accounts</a>.
|
||||||
<p>
|
<p>
|
||||||
Your principal object is....: <%= request.getUserPrincipal() %>
|
Your principal object is....: <%= request.getUserPrincipal() %>
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<sec:authorize url='/secure/index.jsp'>You can currently access "/secure" URLs.</sec:authorize>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<sec:authorize url='/secure/extreme/index.jsp'>You can currently access "/secure/extreme" URLs.</sec:authorize>
|
||||||
|
</p>
|
||||||
|
|
||||||
<p><a href="secure/index.jsp">Secure page</a></p>
|
<p>
|
||||||
|
<a href="secure/index.jsp">Secure page</a></p>
|
||||||
<p><a href="secure/extreme/index.jsp">Extremely secure page</a></p>
|
<p><a href="secure/extreme/index.jsp">Extremely secure page</a></p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -8,6 +8,7 @@ import javax.servlet.ServletContext;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletRequest;
|
||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.jsp.JspException;
|
import javax.servlet.jsp.JspException;
|
||||||
|
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
@ -17,31 +18,43 @@ import org.springframework.security.access.expression.ExpressionUtils;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.web.FilterInvocation;
|
import org.springframework.security.web.FilterInvocation;
|
||||||
|
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||||
import org.springframework.security.web.access.expression.WebSecurityExpressionHandler;
|
import org.springframework.security.web.access.expression.WebSecurityExpressionHandler;
|
||||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expression-based access control tag.
|
* Access control tag which evaluates its body based either on
|
||||||
*
|
* <ul>
|
||||||
|
* <li>an access expression (the "access" attribute), or</li>
|
||||||
|
* <li>by evaluating the current user's right to access a particular URL (set using the "url" attribute).</li>
|
||||||
|
* </ul>
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class AuthorizeTag extends LegacyAuthorizeTag {
|
public class AuthorizeTag extends LegacyAuthorizeTag {
|
||||||
private String access;
|
private String access;
|
||||||
|
private String url;
|
||||||
|
private String method;
|
||||||
|
|
||||||
// If access expression evaluates to "true" return
|
// If access expression evaluates to "true" return
|
||||||
public int doStartTag() throws JspException {
|
public int doStartTag() throws JspException {
|
||||||
if (access == null || access.length() == 0) {
|
|
||||||
return super.doStartTag();
|
|
||||||
}
|
|
||||||
|
|
||||||
Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
|
Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
|
||||||
if (currentUser == null) {
|
if (currentUser == null) {
|
||||||
return SKIP_BODY;
|
return SKIP_BODY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (access != null && access.length() > 0) {
|
||||||
|
return authorizeUsingAccessExpression(currentUser);
|
||||||
|
} else if (url != null && url.length() > 0) {
|
||||||
|
return authorizeUsingUrlCheck(currentUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.doStartTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int authorizeUsingAccessExpression(Authentication currentUser) throws JspException {
|
||||||
// Get web expression
|
// Get web expression
|
||||||
WebSecurityExpressionHandler handler = getExpressionHandler();
|
WebSecurityExpressionHandler handler = getExpressionHandler();
|
||||||
|
|
||||||
|
|
@ -62,10 +75,23 @@ public class AuthorizeTag extends LegacyAuthorizeTag {
|
||||||
return SKIP_BODY;
|
return SKIP_BODY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int authorizeUsingUrlCheck(Authentication currentUser) throws JspException {
|
||||||
|
return getPrivilegeEvaluator().isAllowed(((HttpServletRequest)pageContext.getRequest()).getContextPath(),
|
||||||
|
url, method, currentUser) ? EVAL_BODY_INCLUDE : SKIP_BODY;
|
||||||
|
}
|
||||||
|
|
||||||
public void setAccess(String access) {
|
public void setAccess(String access) {
|
||||||
this.access = access;
|
this.access = access;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUrl(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMethod(String method) {
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
|
|
||||||
WebSecurityExpressionHandler getExpressionHandler() throws JspException {
|
WebSecurityExpressionHandler getExpressionHandler() throws JspException {
|
||||||
ServletContext servletContext = pageContext.getServletContext();
|
ServletContext servletContext = pageContext.getServletContext();
|
||||||
ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
|
ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
|
||||||
|
|
@ -73,12 +99,25 @@ public class AuthorizeTag extends LegacyAuthorizeTag {
|
||||||
|
|
||||||
if (expressionHdlrs.size() == 0) {
|
if (expressionHdlrs.size() == 0) {
|
||||||
throw new JspException("No visible WebSecurityExpressionHandler instance could be found in the application " +
|
throw new JspException("No visible WebSecurityExpressionHandler instance could be found in the application " +
|
||||||
"context. There must be at least one in order to use expressions with taglib support.");
|
"context. There must be at least one in order to support expressions in JSP 'authorize' tags.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return (WebSecurityExpressionHandler) expressionHdlrs.values().toArray()[0];
|
return (WebSecurityExpressionHandler) expressionHdlrs.values().toArray()[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WebInvocationPrivilegeEvaluator getPrivilegeEvaluator() throws JspException {
|
||||||
|
ServletContext servletContext = pageContext.getServletContext();
|
||||||
|
ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
|
||||||
|
Map<String, WebInvocationPrivilegeEvaluator> wipes = ctx.getBeansOfType(WebInvocationPrivilegeEvaluator.class);
|
||||||
|
|
||||||
|
if (wipes.size() == 0) {
|
||||||
|
throw new JspException("No visible WebInvocationPrivilegeEvaluator instance could be found in the application " +
|
||||||
|
"context. There must be at least one in order to support the use of URL access checks in 'authorize' tags.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (WebInvocationPrivilegeEvaluator) wipes.values().toArray()[0];
|
||||||
|
}
|
||||||
|
|
||||||
private static final FilterChain DUMMY_CHAIN = new FilterChain() {
|
private static final FilterChain DUMMY_CHAIN = new FilterChain() {
|
||||||
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
|
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,28 @@
|
||||||
</description>
|
</description>
|
||||||
</attribute>
|
</attribute>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>url</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>false</rtexprvalue>
|
||||||
|
<description>
|
||||||
|
A URL within the application. If the user has access to this URL (as determined by
|
||||||
|
the AccessDecisionManager), the tag body will be evaluated. If not, it will
|
||||||
|
be skipped.
|
||||||
|
</description>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>method</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>false</rtexprvalue>
|
||||||
|
<description>
|
||||||
|
Can optionally be used to narrow down the HTTP method (typically GET or POST) to which the URL
|
||||||
|
applies to. Only has any meaning when used in combination with the "url" attribute.
|
||||||
|
</description>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
|
||||||
<attribute>
|
<attribute>
|
||||||
<name>ifNotGranted</name>
|
<name>ifNotGranted</name>
|
||||||
<required>false</required>
|
<required>false</required>
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,9 @@ import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
import org.springframework.mock.web.MockPageContext;
|
import org.springframework.mock.web.MockPageContext;
|
||||||
import org.springframework.mock.web.MockServletContext;
|
import org.springframework.mock.web.MockServletContext;
|
||||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
import org.springframework.web.context.support.StaticWebApplicationContext;
|
import org.springframework.web.context.support.StaticWebApplicationContext;
|
||||||
|
|
@ -36,6 +38,7 @@ import org.springframework.web.context.support.StaticWebApplicationContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Francois Beausoleil
|
* @author Francois Beausoleil
|
||||||
|
* @author Luke Taylor
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public class AuthorizeTagTests {
|
public class AuthorizeTagTests {
|
||||||
|
|
@ -51,6 +54,7 @@ public class AuthorizeTagTests {
|
||||||
SecurityContextHolder.getContext().setAuthentication(currentUser);
|
SecurityContextHolder.getContext().setAuthentication(currentUser);
|
||||||
StaticWebApplicationContext ctx = new StaticWebApplicationContext();
|
StaticWebApplicationContext ctx = new StaticWebApplicationContext();
|
||||||
ctx.registerSingleton("expressionHandler", DefaultWebSecurityExpressionHandler.class);
|
ctx.registerSingleton("expressionHandler", DefaultWebSecurityExpressionHandler.class);
|
||||||
|
ctx.registerSingleton("wipe", MockWebInvocationPrivilegeEvaluator.class);
|
||||||
MockServletContext servletCtx = new MockServletContext();
|
MockServletContext servletCtx = new MockServletContext();
|
||||||
servletCtx.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ctx);
|
servletCtx.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ctx);
|
||||||
authorizeTag = new AuthorizeTag();
|
authorizeTag = new AuthorizeTag();
|
||||||
|
|
@ -82,6 +86,35 @@ public class AuthorizeTagTests {
|
||||||
authorizeTag.setAccess("permitAll");
|
authorizeTag.setAccess("permitAll");
|
||||||
assertEquals(Tag.EVAL_BODY_INCLUDE, authorizeTag.doStartTag());
|
assertEquals(Tag.EVAL_BODY_INCLUDE, authorizeTag.doStartTag());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// url attribute tests
|
||||||
|
@Test
|
||||||
|
public void skipsBodyWithUrlSetIfNoAuthenticationPresent() throws Exception {
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
authorizeTag.setUrl("/something");
|
||||||
|
assertEquals(Tag.SKIP_BODY, authorizeTag.doStartTag());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void skipsBodyIfUrlIsNotAllowed() throws Exception {
|
||||||
|
authorizeTag.setUrl("/notallowed");
|
||||||
|
assertEquals(Tag.SKIP_BODY, authorizeTag.doStartTag());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void evaluatesBodyIfUrlIsAllowed() throws Exception {
|
||||||
|
authorizeTag.setUrl("/allowed");
|
||||||
|
authorizeTag.setMethod("GET");
|
||||||
|
assertEquals(Tag.EVAL_BODY_INCLUDE, authorizeTag.doStartTag());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void skipsBodyIfMethodIsNotAllowed() throws Exception {
|
||||||
|
authorizeTag.setUrl("/allowed");
|
||||||
|
authorizeTag.setMethod("POST");
|
||||||
|
assertEquals(Tag.SKIP_BODY, authorizeTag.doStartTag());
|
||||||
|
}
|
||||||
|
|
||||||
// Legacy attribute tests
|
// Legacy attribute tests
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -144,4 +177,15 @@ public class AuthorizeTagTests {
|
||||||
authorizeTag.setIfNotGranted("ROLE_TELLER");
|
authorizeTag.setIfNotGranted("ROLE_TELLER");
|
||||||
assertEquals("prevents request - principal has ROLE_TELLER", Tag.SKIP_BODY, authorizeTag.doStartTag());
|
assertEquals("prevents request - principal has ROLE_TELLER", Tag.SKIP_BODY, authorizeTag.doStartTag());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class MockWebInvocationPrivilegeEvaluator implements WebInvocationPrivilegeEvaluator {
|
||||||
|
|
||||||
|
public boolean isAllowed(String uri, Authentication authentication) {
|
||||||
|
return "/allowed".equals(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAllowed(String contextPath, String uri, String method, Authentication authentication) {
|
||||||
|
return "/allowed".equals(uri) && (method == null || "GET".equals(method));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-test</artifactId>
|
<artifactId>spring-test</artifactId>
|
||||||
<optional>true</optional>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>jaxen</groupId>
|
<groupId>jaxen</groupId>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,536 @@
|
||||||
|
/* 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.access;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.RequestDispatcher;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletInputStream;
|
||||||
|
import javax.servlet.ServletOutputStream;
|
||||||
|
import javax.servlet.ServletRequest;
|
||||||
|
import javax.servlet.ServletResponse;
|
||||||
|
import javax.servlet.http.Cookie;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
|
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.web.FilterInvocation;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows users to determine whether they have privileges for a given web URI.
|
||||||
|
*
|
||||||
|
* @author Ben Alex
|
||||||
|
* @author Luke Taylor
|
||||||
|
* @version $Id$
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public class DefaultWebInvocationPrivilegeEvaluator implements WebInvocationPrivilegeEvaluator {
|
||||||
|
//~ Static fields/initializers =====================================================================================
|
||||||
|
|
||||||
|
protected static final Log logger = LogFactory.getLog(DefaultWebInvocationPrivilegeEvaluator.class);
|
||||||
|
|
||||||
|
static final FilterChain DUMMY_CHAIN = new FilterChain() {
|
||||||
|
public void doFilter(ServletRequest req, ServletResponse res) throws IOException, ServletException {
|
||||||
|
throw new UnsupportedOperationException("DefaultWebInvocationPrivilegeEvaluator does not support filter chains");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static final HttpServletResponse DUMMY_RESPONSE = new DummyResponse();
|
||||||
|
|
||||||
|
//~ Instance fields ================================================================================================
|
||||||
|
|
||||||
|
private AbstractSecurityInterceptor securityInterceptor;
|
||||||
|
|
||||||
|
//~ Constructors ===================================================================================================
|
||||||
|
|
||||||
|
public DefaultWebInvocationPrivilegeEvaluator(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 ========================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
if (securityInterceptor.isRejectPublicInvocations()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((authentication == null) || (authentication.getAuthorities() == null)
|
||||||
|
|| authentication.getAuthorities().isEmpty()) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FilterInvocation createFilterInvocation(String contextPath, String uri, String method) {
|
||||||
|
Assert.hasText(contextPath, "contextPath required");
|
||||||
|
Assert.hasText(uri, "URI required");
|
||||||
|
|
||||||
|
DummyRequest req = new DummyRequest();
|
||||||
|
req.setRequestURI(contextPath + uri);
|
||||||
|
req.setContextPath(contextPath);
|
||||||
|
req.setServletPath(null);
|
||||||
|
req.setMethod(method);
|
||||||
|
|
||||||
|
return new FilterInvocation(req, DUMMY_RESPONSE, DUMMY_CHAIN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
class DummyRequest implements HttpServletRequest {
|
||||||
|
private String requestURI;
|
||||||
|
private String contextPath = "";
|
||||||
|
private String servletPath;
|
||||||
|
private String method;
|
||||||
|
|
||||||
|
public void setRequestURI(String requestURI) {
|
||||||
|
this.requestURI = requestURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRequestURI() {
|
||||||
|
return requestURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContextPath(String contextPath) {
|
||||||
|
this.contextPath = contextPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContextPath() {
|
||||||
|
return contextPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServletPath(String servletPath) {
|
||||||
|
this.servletPath = servletPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServletPath() {
|
||||||
|
return servletPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMethod(String method) {
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMethod() {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPathInfo() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getQueryString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthType() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Cookie[] getCookies() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getDateHeader(String name) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHeader(String name) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumeration getHeaderNames() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumeration getHeaders(String name) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIntHeader(String name) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPathTranslated() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRemoteUser() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringBuffer getRequestURL() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRequestedSessionId() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpSession getSession() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpSession getSession(boolean create) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Principal getUserPrincipal() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequestedSessionIdFromCookie() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequestedSessionIdFromURL() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequestedSessionIdFromUrl() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequestedSessionIdValid() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUserInRole(String role) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getAttribute(String name) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumeration getAttributeNames() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCharacterEncoding() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getContentLength() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContentType() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServletInputStream getInputStream() throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLocalAddr() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLocalName() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLocalPort() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Locale getLocale() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumeration getLocales() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParameter(String name) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map getParameterMap() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumeration getParameterNames() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getParameterValues(String name) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProtocol() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BufferedReader getReader() throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRealPath(String path) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRemoteAddr() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRemoteHost() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRemotePort() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RequestDispatcher getRequestDispatcher(String path) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getScheme() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServerName() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getServerPort() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSecure() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeAttribute(String name) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttribute(String name, Object o) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCharacterEncoding(String env) throws UnsupportedEncodingException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DummyResponse implements HttpServletResponse {
|
||||||
|
public void addCookie(Cookie cookie) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDateHeader(String name, long date) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addHeader(String name, String value) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addIntHeader(String name, int value) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsHeader(String name) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String encodeRedirectURL(String url) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String encodeRedirectUrl(String url) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String encodeURL(String url) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String encodeUrl(String url) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendError(int sc) throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendError(int sc, String msg) throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendRedirect(String location) throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDateHeader(String name, long date) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeader(String name, String value) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIntHeader(String name, int value) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(int sc) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(int sc, String sm) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flushBuffer() throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBufferSize() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCharacterEncoding() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContentType() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Locale getLocale() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServletOutputStream getOutputStream() throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrintWriter getWriter() throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCommitted() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetBuffer() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBufferSize(int size) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCharacterEncoding(String charset) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentLength(int len) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContentType(String type) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocale(Locale loc) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -15,60 +15,16 @@
|
||||||
|
|
||||||
package org.springframework.security.web.access;
|
package org.springframework.security.web.access;
|
||||||
|
|
||||||
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.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;
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.web.FilterInvocation;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows users to determine whether they have privileges for a given web URI.
|
* Allows users to determine whether they have privileges for a given web URI.
|
||||||
*
|
*
|
||||||
* @author Ben Alex
|
* @author Luke Taylor
|
||||||
* @version $Id$
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class WebInvocationPrivilegeEvaluator {
|
public interface 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 ========================================================================================================
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether the user represented by the supplied <tt>Authentication</tt> object is
|
* Determines whether the user represented by the supplied <tt>Authentication</tt> object is
|
||||||
|
|
@ -76,9 +32,7 @@ public class WebInvocationPrivilegeEvaluator {
|
||||||
*
|
*
|
||||||
* @param uri the URI excluding the context path (a default context path setting will be used)
|
* @param uri the URI excluding the context path (a default context path setting will be used)
|
||||||
*/
|
*/
|
||||||
public boolean isAllowed(String uri, Authentication authentication) {
|
public boolean isAllowed(String uri, Authentication authentication);
|
||||||
return isAllowed(null, uri, null, authentication);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether the user represented by the supplied <tt>Authentication</tt> object is
|
* Determines whether the user represented by the supplied <tt>Authentication</tt> object is
|
||||||
|
|
@ -90,58 +44,11 @@ public class WebInvocationPrivilegeEvaluator {
|
||||||
* are using a custom <code>FilterInvocationSecurityMetadataSource</code>.
|
* are using a custom <code>FilterInvocationSecurityMetadataSource</code>.
|
||||||
*
|
*
|
||||||
* @param uri the URI excluding the context path
|
* @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 contextPath the context path (may be null).
|
||||||
* @param method the HTTP method (or null, for any method)
|
* @param method the HTTP method (or null, for any method)
|
||||||
* @param authentication the <tt>Authentication</tt> instance whose authorities should be used in evaluation
|
* @param authentication the <tt>Authentication</tt> instance whose authorities should be used in evaluation
|
||||||
* whether access should be granted.
|
* whether access should be granted.
|
||||||
* @return true if access is allowed, false if denied
|
* @return true if access is allowed, false if denied
|
||||||
*/
|
*/
|
||||||
public boolean isAllowed(String contextPath, String uri, String method, Authentication authentication) {
|
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) {
|
|
||||||
if (securityInterceptor.isRejectPublicInvocations()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((authentication == null) || (authentication.getAuthorities() == null)
|
|
||||||
|| authentication.getAuthorities().isEmpty()) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,13 +70,36 @@ public final class UrlUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtains the web application-specific fragment of the URL.
|
* Obtains the web application-specific fragment of the URL.
|
||||||
|
* <p>
|
||||||
|
* Under normal spec conditions,
|
||||||
|
* <pre>
|
||||||
|
* requestURI = contextPath + servletPath + pathInfo
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* But this method may also be called using dummy request objects which just have the requestURI and contextPath
|
||||||
|
* set, for example.
|
||||||
*
|
*
|
||||||
* @return the URL, excluding any server name, context path or servlet path
|
* @return the URL, excluding any server name, context path or servlet path
|
||||||
*/
|
*/
|
||||||
public static String buildRequestUrl(String servletPath, String requestURI, String contextPath, String pathInfo,
|
public static String buildRequestUrl(String servletPath, String requestURI, String contextPath, String pathInfo,
|
||||||
String queryString) {
|
String queryString) {
|
||||||
|
|
||||||
return servletPath + ((pathInfo == null) ? "" : pathInfo) + ((queryString == null) ? "" : ("?" + queryString));
|
StringBuilder url = new StringBuilder();
|
||||||
|
|
||||||
|
if (servletPath != null) {
|
||||||
|
url.append(servletPath);
|
||||||
|
if (pathInfo != null) {
|
||||||
|
url.append(pathInfo);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
url.append(requestURI.substring(contextPath.length()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queryString != null) {
|
||||||
|
url.append("?").append(queryString);
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -34,15 +34,16 @@ import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
|
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
|
||||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||||
|
import org.springframework.security.web.util.UrlUtils;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link org.springframework.security.web.access.WebInvocationPrivilegeEvaluator}.
|
* Tests {@link org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator}.
|
||||||
*
|
*
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public class WebInvocationPrivilegeEvaluatorTests {
|
public class DefaultWebInvocationPrivilegeEvaluatorTests {
|
||||||
private AccessDecisionManager adm;
|
private AccessDecisionManager adm;
|
||||||
private FilterInvocationSecurityMetadataSource ods;
|
private FilterInvocationSecurityMetadataSource ods;
|
||||||
private RunAsManager ram;
|
private RunAsManager ram;
|
||||||
|
|
@ -66,14 +67,14 @@ public class WebInvocationPrivilegeEvaluatorTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void permitsAccessIfNoMatchingAttributesAndPublicInvocationsAllowed() throws Exception {
|
public void permitsAccessIfNoMatchingAttributesAndPublicInvocationsAllowed() throws Exception {
|
||||||
WebInvocationPrivilegeEvaluator wipe = new WebInvocationPrivilegeEvaluator(interceptor);
|
DefaultWebInvocationPrivilegeEvaluator wipe = new DefaultWebInvocationPrivilegeEvaluator(interceptor);
|
||||||
when(ods.getAttributes(anyObject())).thenReturn(null);
|
when(ods.getAttributes(anyObject())).thenReturn(null);
|
||||||
assertTrue(wipe.isAllowed("/context", "/foo/index.jsp", "GET", mock(Authentication.class)));
|
assertTrue(wipe.isAllowed("/context", "/foo/index.jsp", "GET", mock(Authentication.class)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void deniesAccessIfNoMatchingAttributesAndPublicInvocationsNotAllowed() throws Exception {
|
public void deniesAccessIfNoMatchingAttributesAndPublicInvocationsNotAllowed() throws Exception {
|
||||||
WebInvocationPrivilegeEvaluator wipe = new WebInvocationPrivilegeEvaluator(interceptor);
|
DefaultWebInvocationPrivilegeEvaluator wipe = new DefaultWebInvocationPrivilegeEvaluator(interceptor);
|
||||||
when(ods.getAttributes(anyObject())).thenReturn(null);
|
when(ods.getAttributes(anyObject())).thenReturn(null);
|
||||||
interceptor.setRejectPublicInvocations(true);
|
interceptor.setRejectPublicInvocations(true);
|
||||||
assertFalse(wipe.isAllowed("/context", "/foo/index.jsp", "GET", mock(Authentication.class)));
|
assertFalse(wipe.isAllowed("/context", "/foo/index.jsp", "GET", mock(Authentication.class)));
|
||||||
|
|
@ -81,14 +82,14 @@ public class WebInvocationPrivilegeEvaluatorTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void deniesAccessIfAuthenticationIsNull() throws Exception {
|
public void deniesAccessIfAuthenticationIsNull() throws Exception {
|
||||||
WebInvocationPrivilegeEvaluator wipe = new WebInvocationPrivilegeEvaluator(interceptor);
|
DefaultWebInvocationPrivilegeEvaluator wipe = new DefaultWebInvocationPrivilegeEvaluator(interceptor);
|
||||||
assertFalse(wipe.isAllowed("/foo/index.jsp", null));
|
assertFalse(wipe.isAllowed("/foo/index.jsp", null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void allowsAccessIfAccessDecisionMangerDoes() throws Exception {
|
public void allowsAccessIfAccessDecisionMangerDoes() throws Exception {
|
||||||
Authentication token = new TestingAuthenticationToken("test", "Password", "MOCK_INDEX");
|
Authentication token = new TestingAuthenticationToken("test", "Password", "MOCK_INDEX");
|
||||||
WebInvocationPrivilegeEvaluator wipe = new WebInvocationPrivilegeEvaluator(interceptor);
|
DefaultWebInvocationPrivilegeEvaluator wipe = new DefaultWebInvocationPrivilegeEvaluator(interceptor);
|
||||||
assertTrue(wipe.isAllowed("/foo/index.jsp", token));
|
assertTrue(wipe.isAllowed("/foo/index.jsp", token));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,7 +97,7 @@ public class WebInvocationPrivilegeEvaluatorTests {
|
||||||
@Test
|
@Test
|
||||||
public void deniesAccessIfAccessDecisionMangerDoes() throws Exception {
|
public void deniesAccessIfAccessDecisionMangerDoes() throws Exception {
|
||||||
Authentication token = new TestingAuthenticationToken("test", "Password", "MOCK_INDEX");
|
Authentication token = new TestingAuthenticationToken("test", "Password", "MOCK_INDEX");
|
||||||
WebInvocationPrivilegeEvaluator wipe = new WebInvocationPrivilegeEvaluator(interceptor);
|
DefaultWebInvocationPrivilegeEvaluator wipe = new DefaultWebInvocationPrivilegeEvaluator(interceptor);
|
||||||
|
|
||||||
doThrow(new AccessDeniedException("")).when(adm).decide(any(Authentication.class), anyObject(), anyList());
|
doThrow(new AccessDeniedException("")).when(adm).decide(any(Authentication.class), anyObject(), anyList());
|
||||||
|
|
||||||
|
|
@ -105,6 +106,14 @@ public class WebInvocationPrivilegeEvaluatorTests {
|
||||||
|
|
||||||
@Test(expected=UnsupportedOperationException.class)
|
@Test(expected=UnsupportedOperationException.class)
|
||||||
public void dummyChainRejectsInvocation() throws Exception {
|
public void dummyChainRejectsInvocation() throws Exception {
|
||||||
WebInvocationPrivilegeEvaluator.DUMMY_CHAIN.doFilter(mock(HttpServletRequest.class), mock(HttpServletResponse.class));
|
DefaultWebInvocationPrivilegeEvaluator.DUMMY_CHAIN.doFilter(mock(HttpServletRequest.class), mock(HttpServletResponse.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void dummyRequestIsSupportedByUrlUtils() throws Exception {
|
||||||
|
DummyRequest request = new DummyRequest();
|
||||||
|
request.setContextPath("");
|
||||||
|
request.setRequestURI("/something");
|
||||||
|
UrlUtils.buildRequestUrl(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue