Forráskód Böngészése

SEC-309: Patch for Authentication tag to use property of authentication object, rather than invoking an operation on the principal. Allows use of nested properties.

Luke Taylor 17 éve
szülő
commit
10ab4136d1

+ 70 - 98
core/src/main/java/org/springframework/security/taglibs/authz/AuthenticationTag.java

@@ -20,137 +20,109 @@ import org.springframework.security.Authentication;
 import org.springframework.security.context.SecurityContext;
 import org.springframework.security.context.SecurityContextHolder;
 
-import org.springframework.security.userdetails.UserDetails;
+import org.springframework.beans.BeanWrapperImpl;
+import org.springframework.beans.BeansException;
+import org.springframework.web.util.TagUtils;
 
 import java.io.IOException;
 
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import java.util.HashSet;
-import java.util.Set;
-
 import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.PageContext;
 import javax.servlet.jsp.tagext.Tag;
 import javax.servlet.jsp.tagext.TagSupport;
 
-
 /**
  * An {@link javax.servlet.jsp.tagext.Tag} implementation that allows convenient access to the current
- * <code>Authentication</code> object.<p>Whilst JSPs can access the <code>SecurityContext</code> directly, this tag
- * avoids handling <code>null</code> conditions. The tag also properly accommodates
- * <code>Authentication.getPrincipal()</code>, which can either be a <code>String</code> or a
- * <code>UserDetails</code>.</p>
+ * <code>Authentication</code> object. The <tt>operation</tt> attribute
+ * <p>
+ * Whilst JSPs can access the <code>SecurityContext</code> directly, this tag avoids handling <code>null</code> conditions.
  *
- * @author Ben Alex
+ * @author Thomas Champagne
  * @version $Id$
  */
 public class AuthenticationTag extends TagSupport {
-    //~ Static fields/initializers =====================================================================================
-
-    private static final Set methodPrefixValidOptions = new HashSet();
-
-    static {
-        methodPrefixValidOptions.add("get");
-        methodPrefixValidOptions.add("is");
-    }
 
     //~ Instance fields ================================================================================================
 
-    private String methodPrefix = "get";
-    private String operation = "";
-
-    //~ Methods ========================================================================================================
-
-    public int doStartTag() throws JspException {
-        if ((null == operation) || "".equals(operation)) {
-            return Tag.SKIP_BODY;
-        }
-
-        validateArguments();
-
-        if ((SecurityContextHolder.getContext() == null)
-            || !(SecurityContextHolder.getContext() instanceof SecurityContext)
-            || (((SecurityContext) SecurityContextHolder.getContext()).getAuthentication() == null)) {
-            return Tag.SKIP_BODY;
-        }
+    private String var;
+    private String property;
+    private int scope;
+    private boolean scopeSpecified;
 
-        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
 
-        if (auth.getPrincipal() == null) {
-            return Tag.SKIP_BODY;
-        } else if (auth.getPrincipal() instanceof UserDetails) {
-            writeMessage(invokeOperation(auth.getPrincipal()));
-
-            return Tag.SKIP_BODY;
-        } else {
-            writeMessage(auth.getPrincipal().toString());
+    //~ Methods ========================================================================================================
 
-            return Tag.SKIP_BODY;
-        }
+    public AuthenticationTag() {
+        init();
     }
 
-    public String getMethodPrefix() {
-        return methodPrefix;
+    // resets local state
+    private void init() {
+        var = null;
+        scopeSpecified = false;
+        scope = PageContext.PAGE_SCOPE;
     }
-
-    public String getOperation() {
-        return operation;
+    public void setVar(String var) {
+        this.var = var;
     }
 
-    protected String invokeOperation(Object obj) throws JspException {
-        Class clazz = obj.getClass();
-        String methodToInvoke = getOperation();
-        StringBuffer methodName = new StringBuffer();
-        methodName.append(getMethodPrefix());
-        methodName.append(methodToInvoke.substring(0, 1).toUpperCase());
-        methodName.append(methodToInvoke.substring(1));
-
-        Method method = null;
-
-        try {
-            method = clazz.getMethod(methodName.toString(), (Class[]) null);
-        } catch (SecurityException se) {
-            throw new JspException(se);
-        } catch (NoSuchMethodException nsme) {
-            throw new JspException(nsme);
-        }
-
-        Object retVal = null;
-
-        try {
-            retVal = method.invoke(obj, (Object[]) null);
-        } catch (IllegalArgumentException iae) {
-            throw new JspException(iae);
-        } catch (IllegalAccessException iae) {
-            throw new JspException(iae);
-        } catch (InvocationTargetException ite) {
-            throw new JspException(ite);
-        }
-
-        if (retVal == null) {
-            retVal = "";
-        }
-
-        return retVal.toString();
+    public void setProperty(String operation) {
+        this.property = operation;
     }
 
-    public void setMethodPrefix(String methodPrefix) {
-        this.methodPrefix = methodPrefix;
+    public void setScope(String scope) {
+        this.scope = TagUtils.getScope(scope);
+        this.scopeSpecified = true;
     }
 
-    public void setOperation(String operation) {
-        this.operation = operation;
+    public int doStartTag() throws JspException {
+        return super.doStartTag();
     }
 
-    protected void validateArguments() throws JspException {
-        if ((getMethodPrefix() != null) && !getMethodPrefix().equals("")) {
-            if (!methodPrefixValidOptions.contains(getMethodPrefix())) {
-                throw new JspException("Authorization tag : no valid method prefix available");
+    public int doEndTag() throws JspException {
+        Object result = null;
+        // determine the value by...
+        if (property != null) {
+            if ((SecurityContextHolder.getContext() == null)
+                    || !(SecurityContextHolder.getContext() instanceof SecurityContext)
+                    || (SecurityContextHolder.getContext().getAuthentication() == null)) {
+                return Tag.EVAL_PAGE;
+            }
+
+            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+
+            if (auth.getPrincipal() == null) {
+                return Tag.EVAL_PAGE;
+            } else {
+                try {
+                    BeanWrapperImpl wrapper = new BeanWrapperImpl(auth);
+                    result = wrapper.getPropertyValue(property);
+                } catch (BeansException e) {
+                    throw new JspException(e);
+                }
+            }
+        }
+
+        if (var != null) {
+            /*
+             * Store the result, letting an IllegalArgumentException
+             * propagate back if the scope is invalid (e.g., if an attempt
+             * is made to store something in the session without any
+             * HttpSession existing).
+             */
+            if (result != null) {
+                pageContext.setAttribute(var, result, scope);
+            } else {
+                if (scopeSpecified) {
+                    pageContext.removeAttribute(var, scope);
+                } else {
+                    pageContext.removeAttribute(var);
+                }
             }
         } else {
-            throw new JspException("Authorization tag : no method prefix available");
+            writeMessage(result.toString());
         }
+        return EVAL_PAGE;
     }
 
     protected void writeMessage(String msg) throws JspException {

+ 1 - 1
core/src/main/java/org/springframework/security/taglibs/velocity/AuthzImpl.java

@@ -68,7 +68,7 @@ public class AuthzImpl implements Authz {
     public String getPrincipal() {
         MyAuthenticationTag authenticationTag = new MyAuthenticationTag();
 
-        authenticationTag.setOperation("username");
+        authenticationTag.setProperty("username");
 
         try {
             authenticationTag.doStartTag();

+ 37 - 29
core/src/main/resources/org/springframework/security/taglibs/security.tld

@@ -8,8 +8,8 @@
 	<short-name>security</short-name>
 	<uri>http://www.springframework.org/security/tags</uri>
 	<description>
-        Spring Securitys Authorization Tag Library
-		$Id: authz.tld 2176 2007-10-03 14:02:39Z luke_t $
+        Spring Security Authorization Tag Library
+		$Id$
 	</description>
 
 	<tag>
@@ -51,35 +51,43 @@
 		</attribute>
 	</tag>
 
-	<tag>
-		<name>authentication</name>
-		<tag-class>org.springframework.security.taglibs.authz.AuthenticationTag</tag-class>
-		<description>
+    <tag>
+        <name>authentication</name>
+        <tag-class>org.springframework.security.taglibs.authz.AuthenticationTag</tag-class>
+        <description>
             Allows access to the current Authentication object.
-		</description>
-
-		<attribute>
-			<name>operation</name>
-			<required>true</required>
-			<rtexprvalue>true</rtexprvalue>
-			<description>
-                Must be one of the methods of an instance that implements the UserDetails
-                interface. Use the JavaBean style property, you can provide a custom prefix
-                for the method to call.
-			</description>
-		</attribute>
-
-		<attribute>
-			<name>methodPrefix</name>
-			<required>false</required>
-			<rtexprvalue>true</rtexprvalue>
-			<description>
-                Must be get or is. This is used to determine the name of the
-                method to be called. The default is get.
-			</description>
-		</attribute>
-	</tag>
+        </description>
 
+        <attribute>
+            <name>property</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+            <description>
+                Property of the Authentication object which should be output. Supports nested
+                properties. For example if the principal object is an instance of UserDetails,
+                te property "principal.username" will return the username. Alternatively, using
+                "name" will call getName method on the Authentication object directly.
+            </description>
+        </attribute>
+        <attribute>
+            <name>var</name>
+            <required>false</required>
+            <rtexprvalue>false</rtexprvalue>
+            <description>
+                Name of the exported scoped variable for the
+                exception thrown from a nested action. The type of the
+                scoped variable is the type of the exception thrown.
+            </description>
+        </attribute>
+        <attribute>
+            <name>scope</name>
+            <required>false</required>
+            <rtexprvalue>false</rtexprvalue>
+            <description>
+                Scope for var.
+            </description>
+        </attribute>
+    </tag>
 
 	<tag>
 		<name>acl</name>

+ 23 - 43
core/src/test/java/org/springframework/security/taglibs/authz/AuthenticationTagTests.java

@@ -19,11 +19,8 @@ import junit.framework.TestCase;
 
 import org.springframework.security.Authentication;
 import org.springframework.security.GrantedAuthority;
-
 import org.springframework.security.context.SecurityContextHolder;
-
 import org.springframework.security.providers.TestingAuthenticationToken;
-
 import org.springframework.security.userdetails.User;
 
 import javax.servlet.jsp.JspException;
@@ -40,6 +37,8 @@ public class AuthenticationTagTests extends TestCase {
     //~ Instance fields ================================================================================================
 
     private final MyAuthenticationTag authenticationTag = new MyAuthenticationTag();
+    private final Authentication auth = new TestingAuthenticationToken(new User("rodUserDetails", "koala", true, true, true,
+                    true, new GrantedAuthority[] {}), "koala", new GrantedAuthority[] {});
 
     //~ Methods ========================================================================================================
 
@@ -47,86 +46,67 @@ public class AuthenticationTagTests extends TestCase {
         SecurityContextHolder.clearContext();
     }
 
-    public void testOperationAndMethodPrefixWhenPrincipalIsAUserDetailsInstance()
-        throws JspException {
-        Authentication auth = new TestingAuthenticationToken(new User("rodUserDetails", "koala", true, true, true,
-                    true, new GrantedAuthority[] {}), "koala", new GrantedAuthority[] {});
+    public void testOperationWhenPrincipalIsAUserDetailsInstance()throws JspException {
         SecurityContextHolder.getContext().setAuthentication(auth);
 
-        authenticationTag.setOperation("username");
-        authenticationTag.setMethodPrefix("get");
+        authenticationTag.setProperty("name");
         assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
+        assertEquals(Tag.EVAL_PAGE, authenticationTag.doEndTag());
         assertEquals("rodUserDetails", authenticationTag.getLastMessage());
     }
 
     public void testOperationWhenPrincipalIsAString() throws JspException {
-        Authentication auth = new TestingAuthenticationToken("rodAsString", "koala", new GrantedAuthority[] {});
-        SecurityContextHolder.getContext().setAuthentication(auth);
+        SecurityContextHolder.getContext().setAuthentication(
+                new TestingAuthenticationToken("rodAsString", "koala", new GrantedAuthority[] {}));
 
-        authenticationTag.setOperation("principal");
+        authenticationTag.setProperty("principal");
         assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
+        assertEquals(Tag.EVAL_PAGE, authenticationTag.doEndTag());
         assertEquals("rodAsString", authenticationTag.getLastMessage());
     }
 
-    public void testOperationWhenPrincipalIsAUserDetailsInstance()
-        throws JspException {
-        Authentication auth = new TestingAuthenticationToken(new User("rodUserDetails", "koala", true, true, true,
-                    true, new GrantedAuthority[] {}), "koala", new GrantedAuthority[] {});
+    public void testNestedPropertyIsReadCorrectly() throws JspException {
         SecurityContextHolder.getContext().setAuthentication(auth);
 
-        authenticationTag.setOperation("username");
+        authenticationTag.setProperty("principal.username");
         assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
+        assertEquals(Tag.EVAL_PAGE, authenticationTag.doEndTag());
         assertEquals("rodUserDetails", authenticationTag.getLastMessage());
     }
 
     public void testOperationWhenPrincipalIsNull() throws JspException {
-        Authentication auth = new TestingAuthenticationToken(null, "koala", new GrantedAuthority[] {});
-        SecurityContextHolder.getContext().setAuthentication(auth);
+        SecurityContextHolder.getContext().setAuthentication(
+                new TestingAuthenticationToken(null, "koala", new GrantedAuthority[] {}));
 
-        authenticationTag.setOperation("principal");
+        authenticationTag.setProperty("principal");
         assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
+        assertEquals(Tag.EVAL_PAGE, authenticationTag.doEndTag());
     }
 
     public void testOperationWhenSecurityContextIsNull() throws Exception {
         SecurityContextHolder.getContext().setAuthentication(null);
 
-        authenticationTag.setOperation("principal");
+        authenticationTag.setProperty("principal");
         assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
+        assertEquals(Tag.EVAL_PAGE, authenticationTag.doEndTag());
         assertEquals(null, authenticationTag.getLastMessage());
     }
 
     public void testSkipsBodyIfNullOrEmptyOperation() throws Exception {
-        authenticationTag.setOperation("");
-        assertEquals("", authenticationTag.getOperation());
+        authenticationTag.setProperty("");
         assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
+        assertEquals(Tag.EVAL_PAGE, authenticationTag.doEndTag());
     }
 
-    public void testThrowsExceptionForUnrecognisedMethodPrefix() {
-        Authentication auth = new TestingAuthenticationToken(new User("rodUserDetails", "koala", true, true, true,
-                    true, new GrantedAuthority[] {}), "koala", new GrantedAuthority[] {});
-        SecurityContextHolder.getContext().setAuthentication(auth);
-        authenticationTag.setOperation("username");
-        authenticationTag.setMethodPrefix("qrq");
-
-        try {
-            authenticationTag.doStartTag();
-            fail("Should have thrown a JspException");
-        } catch (JspException expected) {
-            assertTrue(true);
-        }
-    }
-
-    public void testThrowsExceptionForUnrecognisedOperation() {
-        Authentication auth = new TestingAuthenticationToken(new User("rodUserDetails", "koala", true, true, true,
-                    true, new GrantedAuthority[] {}), "koala", new GrantedAuthority[] {});
+    public void testThrowsExceptionForUnrecognisedProperty() {
         SecurityContextHolder.getContext().setAuthentication(auth);
-        authenticationTag.setOperation("qsq");
+        authenticationTag.setProperty("qsq");
 
         try {
             authenticationTag.doStartTag();
+            authenticationTag.doEndTag();
             fail("Should have throwns JspException");
         } catch (JspException expected) {
-            assertTrue(true);
         }
     }
 

+ 12 - 8
samples/tutorial/src/main/webapp/index.jsp

@@ -1,14 +1,18 @@
+<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
 <html>
 <body>
 <h1>Home Page</h1>
-Anyone can view this page.<br><br>
-
-If you're logged in, you can <a href="listAccounts.html">list accounts</a>.<br><br>
-
-
-Your principal object is....: <%= request.getUserPrincipal() %><br><br>
+<p>
+Anyone can view this page.
+</p>
+<p>
+If you're logged in, you can <a href="listAccounts.html">list accounts</a>.
+</p>
+<p>
+Your principal object is....: <%= request.getUserPrincipal() %>
+</p>
 
-<p><a href="secure/index.jsp">Secure page</a>
-<p><a href="secure/extreme/index.jsp">Extremely secure page</a>
+<p><a href="secure/index.jsp">Secure page</a></p>
+<p><a href="secure/extreme/index.jsp">Extremely secure page</a></p>
 </body>
 </html>

+ 27 - 6
samples/tutorial/src/main/webapp/secure/index.jsp

@@ -1,13 +1,34 @@
+<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
+
 <html>
 <body>
 <h1>Secure Page</h1>
+<p>
 This is a protected page. You can get to me if you've been remembered,
-or if you've authenticated this session.<br><br>
-
-<%if (request.isUserInRole("ROLE_SUPERVISOR")) { %>
-	You are a supervisor! You can therefore see the <a href="extreme/index.jsp">extremely secure page</a>.<br><br>
-<% } %>
-
+or if you've authenticated this session.
+</p>
+
+<sec:authorize ifAllGranted="ROLE_SUPERVISOR">
+	You are a supervisor! You can therefore see the <a href="extreme/index.jsp">extremely secure page</a>.<br/><br/>
+</sec:authorize>
+
+<h3>Properties obtained using &lt;sec:authentication /&gt; tag</h3>
+<table border="1">
+<tr><th>Tag</th><th>Value</th></tr>
+<tr>
+<td>&lt;sec:authentication property='name' /&gt;</td><td><sec:authentication property="name"/></td>
+</tr>
+<tr>
+<td>&lt;sec:authentication property='principal.username' /&gt;</td><td><sec:authentication property="principal.username"/></td>
+</tr>
+<tr>
+<td>&lt;sec:authentication property='principal.enabled' /&gt;</td><td><sec:authentication property="principal.enabled"/></td>
+</tr>
+<tr>
+<td>&lt;sec:authentication property='principal.accountNonLocked' /&gt;</td><td><sec:authentication property="principal.accountNonLocked"/></td>
+</tr>
+</table>
+
 
 <p><a href="../">Home</a>
 <p><a href="../j_spring_security_logout">Logout</a>