Browse Source

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.

2.0.x
Luke Taylor 18 years ago
parent
commit
10ab4136d1
  1. 168
      core/src/main/java/org/springframework/security/taglibs/authz/AuthenticationTag.java
  2. 2
      core/src/main/java/org/springframework/security/taglibs/velocity/AuthzImpl.java
  3. 66
      core/src/main/resources/org/springframework/security/taglibs/security.tld
  4. 66
      core/src/test/java/org/springframework/security/taglibs/authz/AuthenticationTagTests.java
  5. 20
      samples/tutorial/src/main/webapp/index.jsp
  6. 33
      samples/tutorial/src/main/webapp/secure/index.jsp

168
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.SecurityContext;
import org.springframework.security.context.SecurityContextHolder; 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.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.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.Tag; import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport; import javax.servlet.jsp.tagext.TagSupport;
/** /**
* An {@link javax.servlet.jsp.tagext.Tag} implementation that allows convenient access to the current * 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 * <code>Authentication</code> object. The <tt>operation</tt> attribute
* avoids handling <code>null</code> conditions. The tag also properly accommodates * <p>
* <code>Authentication.getPrincipal()</code>, which can either be a <code>String</code> or a * Whilst JSPs can access the <code>SecurityContext</code> directly, this tag avoids handling <code>null</code> conditions.
* <code>UserDetails</code>.</p>
* *
* @author Ben Alex * @author Thomas Champagne
* @version $Id$ * @version $Id$
*/ */
public class AuthenticationTag extends TagSupport { public class AuthenticationTag extends TagSupport {
//~ Static fields/initializers =====================================================================================
private static final Set methodPrefixValidOptions = new HashSet();
static {
methodPrefixValidOptions.add("get");
methodPrefixValidOptions.add("is");
}
//~ Instance fields ================================================================================================ //~ Instance fields ================================================================================================
private String methodPrefix = "get"; private String var;
private String operation = ""; private String property;
private int scope;
//~ Methods ======================================================================================================== private boolean scopeSpecified;
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;
}
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth.getPrincipal() == null) { //~ Methods ========================================================================================================
return Tag.SKIP_BODY;
} else if (auth.getPrincipal() instanceof UserDetails) {
writeMessage(invokeOperation(auth.getPrincipal()));
return Tag.SKIP_BODY;
} else {
writeMessage(auth.getPrincipal().toString());
return Tag.SKIP_BODY; public AuthenticationTag() {
} init();
} }
public String getMethodPrefix() { // resets local state
return methodPrefix; private void init() {
var = null;
scopeSpecified = false;
scope = PageContext.PAGE_SCOPE;
} }
public void setVar(String var) {
public String getOperation() { this.var = var;
return operation;
} }
protected String invokeOperation(Object obj) throws JspException { public void setProperty(String operation) {
Class clazz = obj.getClass(); this.property = operation;
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 setMethodPrefix(String methodPrefix) { public void setScope(String scope) {
this.methodPrefix = methodPrefix; this.scope = TagUtils.getScope(scope);
this.scopeSpecified = true;
} }
public void setOperation(String operation) { public int doStartTag() throws JspException {
this.operation = operation; return super.doStartTag();
} }
protected void validateArguments() throws JspException { public int doEndTag() throws JspException {
if ((getMethodPrefix() != null) && !getMethodPrefix().equals("")) { Object result = null;
if (!methodPrefixValidOptions.contains(getMethodPrefix())) { // determine the value by...
throw new JspException("Authorization tag : no valid method prefix available"); 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 { } else {
throw new JspException("Authorization tag : no method prefix available"); writeMessage(result.toString());
} }
return EVAL_PAGE;
} }
protected void writeMessage(String msg) throws JspException { protected void writeMessage(String msg) throws JspException {

2
core/src/main/java/org/springframework/security/taglibs/velocity/AuthzImpl.java

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

66
core/src/main/resources/org/springframework/security/taglibs/security.tld

@ -8,8 +8,8 @@
<short-name>security</short-name> <short-name>security</short-name>
<uri>http://www.springframework.org/security/tags</uri> <uri>http://www.springframework.org/security/tags</uri>
<description> <description>
Spring Securitys Authorization Tag Library Spring Security Authorization Tag Library
$Id: authz.tld 2176 2007-10-03 14:02:39Z luke_t $ $Id$
</description> </description>
<tag> <tag>
@ -51,35 +51,43 @@
</attribute> </attribute>
</tag> </tag>
<tag> <tag>
<name>authentication</name> <name>authentication</name>
<tag-class>org.springframework.security.taglibs.authz.AuthenticationTag</tag-class> <tag-class>org.springframework.security.taglibs.authz.AuthenticationTag</tag-class>
<description> <description>
Allows access to the current Authentication object. Allows access to the current Authentication object.
</description> </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>
<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> <tag>
<name>acl</name> <name>acl</name>

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

20
samples/tutorial/src/main/webapp/index.jsp

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

33
samples/tutorial/src/main/webapp/secure/index.jsp

@ -1,13 +1,34 @@
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<html> <html>
<body> <body>
<h1>Secure Page</h1> <h1>Secure Page</h1>
<p>
This is a protected page. You can get to me if you've been remembered, This is a protected page. You can get to me if you've been remembered,
or if you've authenticated this session.<br><br> or if you've authenticated this session.
</p>
<%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> <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="../">Home</a>
<p><a href="../j_spring_security_logout">Logout</a> <p><a href="../j_spring_security_logout">Logout</a>

Loading…
Cancel
Save