Browse Source

SEC-524: Added "var" attribute to authorize and accesscontrollist JSP tags.

Allows the result of the boolean condition granting/denying access to be stored in the page context for later use, without having to duplicate the tag.
Luke Taylor 15 years ago
parent
commit
bf91f2ca67

+ 1 - 0
itest/web/src/main/resources/log4j.properties

@@ -7,4 +7,5 @@ log4j.appender.stdout.layout.ConversionPattern=%d %p %c - %m%n
 log4j.category.org.apache.jasper=INFO
 log4j.category.org.apache.directory=ERROR
 log4j.category.org.mortbay.log=INFO
+log4j.category.httpclient.wire=INFO
 log4j.category.org.springframework.security=TRACE

+ 3 - 3
itest/web/src/main/webapp/WEB-INF/http-security.xml

@@ -11,10 +11,10 @@
        Needs to be supplemented with authentication provider(s)
     -->
 
-    <http>
+    <http use-expressions="true">
         <intercept-url pattern="/login.jsp*" filters="none" />
-        <intercept-url pattern="/secure/**" access="ROLE_DEVELOPER,ROLE_USER" />
-        <intercept-url pattern="/**" access="ROLE_DEVELOPER,ROLE_USER" />
+        <intercept-url pattern="/secure/**" access="hasAnyRole('ROLE_DEVELOPER','ROLE_USER')" />
+        <intercept-url pattern="/**" access="hasAnyRole('ROLE_DEVELOPER','ROLE_USER')" />
 
         <form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=true"/>
         <http-basic/>

+ 1 - 0
itest/web/src/main/webapp/WEB-INF/in-memory-provider.xml

@@ -12,6 +12,7 @@
               <user name="miles" password="milespassword" authorities="ROLE_USER,ROLE_JAZZ,ROLE_TRUMPETER"/>
               <user name="johnc" password="johncspassword" authorities="ROLE_USER,ROLE_JAZZ,ROLE_SAXOPHONIST"/>
               <user name="jimi" password="jimispassword" authorities="ROLE_USER,ROLE_ROCK,ROLE_GUITARIST"/>
+              <user name="bessie" password="bessiespassword" authorities="ROLE_USER,ROLE_JAZZ,ROLE_SINGER"/>
               <user name="theescapist&lt;&gt;&amp;." password="theescapistspassword" authorities="ROLE_USER"/>
             </user-service>
         </authentication-provider>

+ 18 - 1
itest/web/src/main/webapp/WEB-INF/security.tld

@@ -9,7 +9,6 @@
     <uri>http://www.springframework.org/security/tags</uri>
     <description>
         Spring Security Authorization Tag Library
-        $Id$
     </description>
 
     <tag>
@@ -51,6 +50,15 @@
             </description>
         </attribute>
 
+        <attribute>
+            <name>var</name>
+            <required>false</required>
+            <rtexprvalue>false</rtexprvalue>
+            <description>
+                A page scoped variable into which the boolean result of the tag evaluation will be written, allowing the
+                same condition to be reused subsequently in the page without re-evaluation.
+            </description>
+        </attribute>
 
         <attribute>
             <name>ifNotGranted</name>
@@ -153,6 +161,15 @@
                 are being evaluated.
             </description>
         </attribute>
+        <attribute>
+            <name>var</name>
+            <required>false</required>
+            <rtexprvalue>false</rtexprvalue>
+            <description>
+                A page scoped variable into which the boolean result of the tag evaluation will be written, allowing the
+                same condition to be reused subsequently in the page without re-evaluation.
+            </description>
+        </attribute>
     </tag>
 
 </taglib>

+ 21 - 0
itest/web/src/main/webapp/secure/authorizationTagTestPage.jsp

@@ -0,0 +1,21 @@
+<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
+<html>
+<body>
+<h1>Authorization Tag Test Page</h1>
+
+<sec:authorize access="hasRole('ROLE_USER')" var="allowed">
+Users can see this and 'allowed' variable is ${allowed}.
+</sec:authorize>
+
+<sec:authorize access="hasRole('ROLE_X')" var="allowed">
+Role X users (nobody) can see this.
+</sec:authorize>
+
+Role X expression evaluates to ${allowed}.
+
+
+</body>
+
+</html>
+
+

+ 0 - 13
itest/web/src/test/java/org/springframework/security/integration/InMemoryProviderWebAppTests.java

@@ -94,17 +94,4 @@ public class InMemoryProviderWebAppTests extends AbstractWebServerIntegrationTes
         tester.gotoPage("secure/index.html");
         tester.assertTextPresent("This session has been expired");
     }
-
-    @Test
-    public void authenticationTagEscapingWorksCorrectly() {
-        beginAt("secure/authenticationTagTestPage.jsp");
-        login("theescapist<>&.", "theescapistspassword");
-        String response = tester.getServerResponse();
-        assertTrue(response.contains("This is the unescaped authentication name: theescapist<>&."));
-        assertTrue(response.contains("This is the unescaped principal.username: theescapist<>&."));
-        assertTrue(response.contains("This is the authentication name: theescapist&lt;&gt;&amp;&#46;"));
-        assertTrue(response.contains("This is the principal.username: theescapist&lt;&gt;&amp;&#46;"));
-    }
-
-
 }

+ 39 - 0
itest/web/src/test/java/org/springframework/security/integration/JspTaglibTests.java

@@ -0,0 +1,39 @@
+package org.springframework.security.integration;
+
+import static org.testng.Assert.*;
+
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Luke Taylor
+ */
+public final class JspTaglibTests extends AbstractWebServerIntegrationTests {
+
+    @Override
+    protected String getContextConfigLocations() {
+        return "/WEB-INF/http-security.xml /WEB-INF/in-memory-provider.xml";
+    }
+
+    @Test
+    public void authenticationTagEscapingWorksCorrectly() {
+        beginAt("secure/authenticationTagTestPage.jsp");
+        login("theescapist<>&.", "theescapistspassword");
+        String response = tester.getServerResponse();
+        assertTrue(response.contains("This is the unescaped authentication name: theescapist<>&."));
+        assertTrue(response.contains("This is the unescaped principal.username: theescapist<>&."));
+        assertTrue(response.contains("This is the authentication name: theescapist&lt;&gt;&amp;&#46;"));
+        assertTrue(response.contains("This is the principal.username: theescapist&lt;&gt;&amp;&#46;"));
+    }
+
+    @Test
+    public void authorizationTagEvaluatesExpressionCorrectlyAndWritesValueToVariable() {
+        beginAt("secure/authorizationTagTestPage.jsp");
+        login("bessie", "bessiespassword");
+        String response = tester.getServerResponse();
+        assertTrue(response.contains("Users can see this and 'allowed' variable is true."));
+        assertFalse(response.contains("Role X users (nobody) can see this."));
+        assertTrue(response.contains("Role X expression evaluates to false"));
+    }
+
+}

+ 27 - 6
taglibs/src/main/java/org/springframework/security/taglibs/authz/AccessControlListTag.java

@@ -66,6 +66,7 @@ import org.springframework.web.util.ExpressionEvaluationUtils;
  * implementations are found in the application context.
  *
  * @author Ben Alex
+ * @author Luke Taylor
  */
 public class AccessControlListTag extends TagSupport {
     //~ Static fields/initializers =====================================================================================
@@ -81,12 +82,13 @@ public class AccessControlListTag extends TagSupport {
     private SidRetrievalStrategy sidRetrievalStrategy;
     private PermissionFactory permissionFactory;
     private String hasPermission = "";
+    private String var;
 
     //~ Methods ========================================================================================================
 
     public int doStartTag() throws JspException {
         if ((null == hasPermission) || "".equals(hasPermission)) {
-            return Tag.SKIP_BODY;
+            return skipBody();
         }
 
         initializeIfRequired();
@@ -111,7 +113,7 @@ public class AccessControlListTag extends TagSupport {
             }
 
             // Of course they have access to a null object!
-            return Tag.EVAL_BODY_INCLUDE;
+            return evalBody();
         }
 
         if (SecurityContextHolder.getContext().getAuthentication() == null) {
@@ -120,7 +122,7 @@ public class AccessControlListTag extends TagSupport {
                     "SecurityContextHolder did not return a non-null Authentication object, so skipping tag body");
             }
 
-            return Tag.SKIP_BODY;
+            return skipBody();
         }
 
         List<Sid> sids = sidRetrievalStrategy.getSids(SecurityContextHolder.getContext().getAuthentication());
@@ -131,15 +133,30 @@ public class AccessControlListTag extends TagSupport {
             Acl acl = aclService.readAclById(oid, sids);
 
             if (acl.isGranted(requiredPermissions, sids, false)) {
-                return Tag.EVAL_BODY_INCLUDE;
+                return evalBody();
             } else {
-                return Tag.SKIP_BODY;
+                return skipBody();
             }
         } catch (NotFoundException nfe) {
-            return Tag.SKIP_BODY;
+            return skipBody();
         }
     }
 
+    private int skipBody() {
+        if (var != null) {
+            pageContext.setAttribute(var, Boolean.FALSE, PageContext.PAGE_SCOPE);
+        }
+        return SKIP_BODY;
+    }
+
+    private int evalBody() {
+        if (var != null) {
+            pageContext.setAttribute(var, Boolean.TRUE, PageContext.PAGE_SCOPE);
+        }
+        return EVAL_BODY_INCLUDE;
+    }
+
+
     /**
      * Allows test cases to override where application context obtained from.
      *
@@ -233,4 +250,8 @@ public class AccessControlListTag extends TagSupport {
     public void setHasPermission(String hasPermission) {
         this.hasPermission = hasPermission;
     }
+
+    public void setVar(String var) {
+        this.var = var;
+    }
 }

+ 17 - 3
taglibs/src/main/java/org/springframework/security/taglibs/authz/AuthorizeTag.java

@@ -10,6 +10,7 @@ import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.PageContext;
 
 import org.springframework.context.ApplicationContext;
 import org.springframework.expression.Expression;
@@ -35,6 +36,7 @@ public class AuthorizeTag extends LegacyAuthorizeTag {
     private String access;
     private String url;
     private String method;
+    private String var;
 
     // If access expression evaluates to "true" return
     public int doStartTag() throws JspException {
@@ -44,13 +46,21 @@ public class AuthorizeTag extends LegacyAuthorizeTag {
             return SKIP_BODY;
         }
 
+        int result;
+
         if (access != null && access.length() > 0) {
-            return authorizeUsingAccessExpression(currentUser);
+            result = authorizeUsingAccessExpression(currentUser);
         } else if (url != null && url.length() > 0) {
-            return authorizeUsingUrlCheck(currentUser);
+            result = authorizeUsingUrlCheck(currentUser);
+        } else {
+            result = super.doStartTag();
+        }
+
+        if (var != null) {
+            pageContext.setAttribute(var, Boolean.valueOf(result == EVAL_BODY_INCLUDE), PageContext.PAGE_SCOPE);
         }
 
-        return super.doStartTag();
+        return result;
     }
 
     private int authorizeUsingAccessExpression(Authentication currentUser) throws JspException {
@@ -91,6 +101,10 @@ public class AuthorizeTag extends LegacyAuthorizeTag {
         this.method = method;
     }
 
+    public void setVar(String var) {
+        this.var = var;
+    }
+
     WebSecurityExpressionHandler getExpressionHandler() throws JspException {
         ServletContext servletContext = pageContext.getServletContext();
         ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);

+ 18 - 1
taglibs/src/main/resources/META-INF/security.tld

@@ -9,7 +9,6 @@
     <uri>http://www.springframework.org/security/tags</uri>
     <description>
         Spring Security Authorization Tag Library
-        $Id$
     </description>
 
     <tag>
@@ -51,6 +50,15 @@
             </description>
         </attribute>
 
+        <attribute>
+            <name>var</name>
+            <required>false</required>
+            <rtexprvalue>false</rtexprvalue>
+            <description>
+                A page scoped variable into which the boolean result of the tag evaluation will be written, allowing the
+                same condition to be reused subsequently in the page without re-evaluation.
+            </description>
+        </attribute>
 
         <attribute>
             <name>ifNotGranted</name>
@@ -153,6 +161,15 @@
                 are being evaluated.
             </description>
         </attribute>
+        <attribute>
+            <name>var</name>
+            <required>false</required>
+            <rtexprvalue>false</rtexprvalue>
+            <description>
+                A page scoped variable into which the boolean result of the tag evaluation will be written, allowing the
+                same condition to be reused subsequently in the page without re-evaluation.
+            </description>
+        </attribute>
     </tag>
 
 </taglib>

+ 8 - 5
taglibs/src/test/java/org/springframework/security/taglibs/authz/AccessControlListTagTests.java

@@ -1,6 +1,6 @@
 package org.springframework.security.taglibs.authz;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
 import static org.mockito.Matchers.*;
 import static org.mockito.Mockito.*;
 
@@ -33,6 +33,7 @@ import org.springframework.web.context.WebApplicationContext;
 public class AccessControlListTagTests {
     AccessControlListTag tag;
     Acl acl;
+    MockPageContext pageContext;
 
     @Before
     public void setup() {
@@ -44,9 +45,6 @@ public class AccessControlListTagTests {
         ObjectIdentity oid = mock(ObjectIdentity.class);
         ObjectIdentityRetrievalStrategy oidStrategy = mock(ObjectIdentityRetrievalStrategy.class);
         when(oidStrategy.getObjectIdentity(anyObject())).thenReturn(oid);
-//        AclPermissionEvaluator pe = new AclPermissionEvaluator(service);
-//        pe.setObjectIdentityRetrievalStrategy(oidStrategy);
-//        pe.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
         acl = mock(Acl.class);
 
         when(service.readAclById(any(ObjectIdentity.class), anyList())).thenReturn(acl);
@@ -59,7 +57,8 @@ public class AccessControlListTagTests {
 
         MockServletContext servletCtx = new MockServletContext();
         servletCtx.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ctx);
-        tag.setPageContext(new MockPageContext(servletCtx, new MockHttpServletRequest(), new MockHttpServletResponse()));
+        pageContext = new MockPageContext(servletCtx, new MockHttpServletRequest(), new MockHttpServletResponse());
+        tag.setPageContext(pageContext);
     }
 
     @After
@@ -73,8 +72,10 @@ public class AccessControlListTagTests {
 
         tag.setDomainObject(new Object());
         tag.setHasPermission("READ");
+        tag.setVar("allowed");
 
         assertEquals(Tag.EVAL_BODY_INCLUDE, tag.doStartTag());
+        assertTrue((Boolean)pageContext.getAttribute("allowed"));
     }
 
     @Test
@@ -83,8 +84,10 @@ public class AccessControlListTagTests {
 
         tag.setDomainObject(new Object());
         tag.setHasPermission("READ");
+        tag.setVar("allowed");
 
         assertEquals(Tag.SKIP_BODY, tag.doStartTag());
+        assertFalse((Boolean)pageContext.getAttribute("allowed"));
     }
 
 }