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:
@@ -8,6 +8,7 @@ import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.jsp.JspException;
|
||||
|
||||
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.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.expression.WebSecurityExpressionHandler;
|
||||
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
|
||||
* @version $Id$
|
||||
* @since 3.0
|
||||
*/
|
||||
public class AuthorizeTag extends LegacyAuthorizeTag {
|
||||
private String access;
|
||||
private String url;
|
||||
private String method;
|
||||
|
||||
// If access expression evaluates to "true" return
|
||||
public int doStartTag() throws JspException {
|
||||
if (access == null || access.length() == 0) {
|
||||
return super.doStartTag();
|
||||
}
|
||||
|
||||
Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
if (currentUser == null) {
|
||||
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
|
||||
WebSecurityExpressionHandler handler = getExpressionHandler();
|
||||
|
||||
@@ -62,10 +75,23 @@ public class AuthorizeTag extends LegacyAuthorizeTag {
|
||||
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) {
|
||||
this.access = access;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
WebSecurityExpressionHandler getExpressionHandler() throws JspException {
|
||||
ServletContext servletContext = pageContext.getServletContext();
|
||||
ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
|
||||
@@ -73,12 +99,25 @@ public class AuthorizeTag extends LegacyAuthorizeTag {
|
||||
|
||||
if (expressionHdlrs.size() == 0) {
|
||||
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];
|
||||
}
|
||||
|
||||
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() {
|
||||
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
@@ -30,6 +30,28 @@
|
||||
</description>
|
||||
</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>
|
||||
<name>ifNotGranted</name>
|
||||
<required>false</required>
|
||||
|
||||
+44
@@ -28,7 +28,9 @@ import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockPageContext;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
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.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.StaticWebApplicationContext;
|
||||
@@ -36,6 +38,7 @@ import org.springframework.web.context.support.StaticWebApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Francois Beausoleil
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class AuthorizeTagTests {
|
||||
@@ -51,6 +54,7 @@ public class AuthorizeTagTests {
|
||||
SecurityContextHolder.getContext().setAuthentication(currentUser);
|
||||
StaticWebApplicationContext ctx = new StaticWebApplicationContext();
|
||||
ctx.registerSingleton("expressionHandler", DefaultWebSecurityExpressionHandler.class);
|
||||
ctx.registerSingleton("wipe", MockWebInvocationPrivilegeEvaluator.class);
|
||||
MockServletContext servletCtx = new MockServletContext();
|
||||
servletCtx.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ctx);
|
||||
authorizeTag = new AuthorizeTag();
|
||||
@@ -82,6 +86,35 @@ public class AuthorizeTagTests {
|
||||
authorizeTag.setAccess("permitAll");
|
||||
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
|
||||
|
||||
@Test
|
||||
@@ -144,4 +177,15 @@ public class AuthorizeTagTests {
|
||||
authorizeTag.setIfNotGranted("ROLE_TELLER");
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user