From 9be82a3d8f114b2cdd80e3578f6989216a284166 Mon Sep 17 00:00:00 2001 From: Ben Alex Date: Thu, 3 Nov 2005 13:51:55 +0000 Subject: [PATCH] SEC-67: Enhance taglib to allow retrieval of custom UserDetails methods. --- .../taglibs/authz/AuthenticationTag.java | 83 +++++++++++++++++-- .../org/acegisecurity/taglibs/authz.tld | 15 +++- .../taglibs/authz/AuthenticationTagTests.java | 38 ++++++++- 3 files changed, 127 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/acegisecurity/taglibs/authz/AuthenticationTag.java b/core/src/main/java/org/acegisecurity/taglibs/authz/AuthenticationTag.java index 706181bfc0..2e37bf7c07 100644 --- a/core/src/main/java/org/acegisecurity/taglibs/authz/AuthenticationTag.java +++ b/core/src/main/java/org/acegisecurity/taglibs/authz/AuthenticationTag.java @@ -17,10 +17,17 @@ package net.sf.acegisecurity.taglibs.authz; import net.sf.acegisecurity.Authentication; import net.sf.acegisecurity.UserDetails; +import net.sf.acegisecurity.context.SecurityContext; import net.sf.acegisecurity.context.SecurityContextHolder; 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.tagext.Tag; import javax.servlet.jsp.tagext.TagSupport; @@ -43,14 +50,28 @@ import javax.servlet.jsp.tagext.TagSupport; public class AuthenticationTag extends TagSupport { //~ Static fields/initializers ============================================= - public static final String OPERATION_PRINCIPAL = "principal"; + private final static Set methodPrefixValidOptions = new HashSet(); + + static { + methodPrefixValidOptions.add("get"); + methodPrefixValidOptions.add("is"); + } //~ Instance fields ======================================================== + private String methodPrefix = "get"; private String operation = ""; //~ Methods ================================================================ + public void setMethodPrefix(String methodPrefix) { + this.methodPrefix = methodPrefix; + } + + public String getMethodPrefix() { + return methodPrefix; + } + public void setOperation(String operation) { this.operation = operation; } @@ -64,11 +85,12 @@ public class AuthenticationTag extends TagSupport { return Tag.SKIP_BODY; } - if (!OPERATION_PRINCIPAL.equalsIgnoreCase(operation)) { - throw new JspException("Unsupported use of auth:authentication tag"); - } + validateArguments(); - if (SecurityContextHolder.getContext().getAuthentication() == null) { + if ((SecurityContextHolder.getContext() == null) + || !(SecurityContextHolder.getContext() instanceof SecurityContext) + || (((SecurityContext) SecurityContextHolder.getContext()) + .getAuthentication() == null)) { return Tag.SKIP_BODY; } @@ -78,7 +100,7 @@ public class AuthenticationTag extends TagSupport { if (auth.getPrincipal() == null) { return Tag.SKIP_BODY; } else if (auth.getPrincipal() instanceof UserDetails) { - writeMessage(((UserDetails) auth.getPrincipal()).getUsername()); + writeMessage(invokeOperation(auth.getPrincipal())); return Tag.SKIP_BODY; } else { @@ -88,6 +110,55 @@ public class AuthenticationTag extends TagSupport { } } + 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.getDeclaredMethod(methodName.toString(), null); + } catch (SecurityException se) { + throw new JspException(se); + } catch (NoSuchMethodException nsme) { + throw new JspException(nsme); + } + + Object retVal = null; + + try { + retVal = method.invoke(obj, 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(); + } + + protected void validateArguments() throws JspException { + if ((getMethodPrefix() != null) && !getMethodPrefix().equals("")) { + if (!methodPrefixValidOptions.contains(getMethodPrefix())) { + throw new JspException( + "Authorization tag : no valid method prefix available"); + } + } else { + throw new JspException( + "Authorization tag : no method prefix available"); + } + } + protected void writeMessage(String msg) throws JspException { try { pageContext.getOut().write(String.valueOf(msg)); diff --git a/core/src/main/resources/org/acegisecurity/taglibs/authz.tld b/core/src/main/resources/org/acegisecurity/taglibs/authz.tld index e46f254b16..4b298cec4b 100644 --- a/core/src/main/resources/org/acegisecurity/taglibs/authz.tld +++ b/core/src/main/resources/org/acegisecurity/taglibs/authz.tld @@ -63,10 +63,21 @@ true true - Must be "principal", for a String representation of the - username. An attribute to aid in future extension of the tag. + 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. + + + methodPrefix + false + true + + Must be get or is. This is used to determine the name of the + method to be called. The default is get. + + diff --git a/core/src/test/java/org/acegisecurity/taglibs/authz/AuthenticationTagTests.java b/core/src/test/java/org/acegisecurity/taglibs/authz/AuthenticationTagTests.java index 0f963fdeba..646a4b17bf 100644 --- a/core/src/test/java/org/acegisecurity/taglibs/authz/AuthenticationTagTests.java +++ b/core/src/test/java/org/acegisecurity/taglibs/authz/AuthenticationTagTests.java @@ -40,6 +40,20 @@ public class AuthenticationTagTests extends TestCase { //~ Methods ================================================================ + public void testOperationAndMethodPrefixWhenPrincipalIsAUserDetailsInstance() + throws JspException { + Authentication auth = new TestingAuthenticationToken(new User( + "marissaUserDetails", "koala", true, true, true, true, + new GrantedAuthority[] {}), "koala", + new GrantedAuthority[] {}); + SecurityContextHolder.getContext().setAuthentication(auth); + + authenticationTag.setOperation("username"); + authenticationTag.setMethodPrefix("get"); + assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag()); + assertEquals("marissaUserDetails", authenticationTag.getLastMessage()); + } + public void testOperationWhenPrincipalIsAString() throws JspException { Authentication auth = new TestingAuthenticationToken("marissaAsString", "koala", new GrantedAuthority[] {}); @@ -58,7 +72,7 @@ public class AuthenticationTagTests extends TestCase { new GrantedAuthority[] {}); SecurityContextHolder.getContext().setAuthentication(auth); - authenticationTag.setOperation("principal"); + authenticationTag.setOperation("username"); assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag()); assertEquals("marissaUserDetails", authenticationTag.getLastMessage()); } @@ -89,7 +103,29 @@ public class AuthenticationTagTests extends TestCase { assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag()); } + public void testThrowsExceptionForUnrecognisedMethodPrefix() { + Authentication auth = new TestingAuthenticationToken(new User( + "marissaUserDetails", "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( + "marissaUserDetails", "koala", true, true, true, true, + new GrantedAuthority[] {}), "koala", + new GrantedAuthority[] {}); + SecurityContextHolder.getContext().setAuthentication(auth); authenticationTag.setOperation("qsq"); try {