diff --git a/build.xml b/build.xml index 35b1ecb5ca..448af3f2f4 100644 --- a/build.xml +++ b/build.xml @@ -109,18 +109,33 @@ - + + - + + + + + + + + + + + + + + @@ -131,7 +146,7 @@ - + @@ -149,7 +164,7 @@ - + @@ -166,7 +181,7 @@ - + @@ -183,7 +198,7 @@ - + @@ -305,7 +320,7 @@ diff --git a/core/src/main/java/org/acegisecurity/taglibs/authz/AuthorizeTag.java b/core/src/main/java/org/acegisecurity/taglibs/authz/AuthorizeTag.java new file mode 100644 index 0000000000..a8fd200f17 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/taglibs/authz/AuthorizeTag.java @@ -0,0 +1,132 @@ +/* + * The Acegi Security System for Spring is published under the terms + * of the Apache Software License. + * + * Visit http://acegisecurity.sourceforge.net for further details. + */ + +package net.sf.acegisecurity.taglibs.authz; + +import net.sf.acegisecurity.Authentication; +import net.sf.acegisecurity.GrantedAuthorityImpl; +import net.sf.acegisecurity.context.ContextHolder; +import net.sf.acegisecurity.context.SecureContext; + +import java.util.*; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.tagext.Tag; +import javax.servlet.jsp.tagext.TagSupport; + + +/** + * An implementation of {@link javax.servlet.jsp.tagext.Tag} that allows it's + * body through if some authorizations are granted to the request's principal. + * + * @author Francois Beausoleil + * @version $Id$ + */ +public class AuthorizeTag extends TagSupport { + //~ Instance fields ======================================================== + + private String ifAllGranted = ""; + private String ifAnyGranted = ""; + private String ifNotGranted = ""; + + //~ Methods ================================================================ + + public void setIfAllGranted(String ifAllGranted) { + this.ifAllGranted = ifAllGranted; + } + + public String getIfAllGranted() { + return ifAllGranted; + } + + public void setIfAnyGranted(String ifAnyGranted) { + this.ifAnyGranted = ifAnyGranted; + } + + public String getIfAnyGranted() { + return ifAnyGranted; + } + + public void setIfNotGranted(String ifNotGranted) { + this.ifNotGranted = ifNotGranted; + } + + public String getIfNotGranted() { + return ifNotGranted; + } + + public int doStartTag() throws JspException { + if (((null == ifAllGranted) || "".equals(ifAllGranted)) + && ((null == ifAnyGranted) || "".equals(ifAnyGranted)) + && ((null == ifNotGranted) || "".equals(ifNotGranted))) { + return Tag.SKIP_BODY; + } + + final Collection granted = getPrincipalAuthorities(); + + if ((null != ifNotGranted) && !"".equals(ifNotGranted)) { + Set grantedCopy = retainAll(granted, + parseAuthoritiesString(ifNotGranted)); + + if (!grantedCopy.isEmpty()) { + return Tag.SKIP_BODY; + } + } + + if ((null != ifAllGranted) && !"".equals(ifAllGranted)) { + if (!granted.containsAll(parseAuthoritiesString(ifAllGranted))) { + return Tag.SKIP_BODY; + } + } + + if ((null != ifAnyGranted) && !"".equals(ifAnyGranted)) { + Set grantedCopy = retainAll(granted, + parseAuthoritiesString(ifAnyGranted)); + + if (grantedCopy.isEmpty()) { + return Tag.SKIP_BODY; + } + } + + return Tag.EVAL_BODY_INCLUDE; + } + + private Collection getPrincipalAuthorities() { + SecureContext context = ((SecureContext) ContextHolder.getContext()); + + if (null == context) { + return Collections.EMPTY_LIST; + } + + Authentication currentUser = context.getAuthentication(); + + Collection granted = Arrays.asList(currentUser.getAuthorities()); + + return granted; + } + + private Set parseAuthoritiesString(String authorizationsString) { + final Set requiredAuthorities = new HashSet(); + final StringTokenizer tokenizer; + tokenizer = new StringTokenizer(authorizationsString, ",", false); + + while (tokenizer.hasMoreTokens()) { + String role = tokenizer.nextToken(); + requiredAuthorities.add(new GrantedAuthorityImpl(role)); + } + + return requiredAuthorities; + } + + private Set retainAll(final Collection granted, + final Set requiredAuthorities) { + Set grantedCopy = new HashSet(granted); + grantedCopy.retainAll(requiredAuthorities); + + return grantedCopy; + } +} diff --git a/core/src/main/resources/org/acegisecurity/taglibs/authz.tld b/core/src/main/resources/org/acegisecurity/taglibs/authz.tld new file mode 100644 index 0000000000..e26678ef2d --- /dev/null +++ b/core/src/main/resources/org/acegisecurity/taglibs/authz.tld @@ -0,0 +1,53 @@ + + + + 1.0 + 1.2 + authz + http://acegisecurity.sf.net/authz + + Acegi Security Systems Authorization Tag Library + $Id$ + + + + authorize + net.sf.acegisecurity.taglibs.authz.AuthorizeTag + + A simple tag to output or not the body of the tag if the principal + has or doesn't have certain authorities. + + + + ifNotGranted + false + true + + A comma separated list of roles which the user must not have + for the body to be output. + + + + + ifAllGranted + false + true + + A comma separated list of roles which the user must all + possess for the body to be output. + + + + + ifAnyGranted + false + true + + A comma separated list of roles, one of which the user must + possess for the body to be output. + + + + diff --git a/core/src/test/java/org/acegisecurity/taglibs/authz/AuthorizeTagAttributeTests.java b/core/src/test/java/org/acegisecurity/taglibs/authz/AuthorizeTagAttributeTests.java new file mode 100644 index 0000000000..6e36ec72e3 --- /dev/null +++ b/core/src/test/java/org/acegisecurity/taglibs/authz/AuthorizeTagAttributeTests.java @@ -0,0 +1,71 @@ +/* + * The Acegi Security System for Spring is published under the terms + * of the Apache Software License. + * + * Visit http://acegisecurity.sourceforge.net for further details. + */ + +package net.sf.acegisecurity.taglibs.authz; + +import junit.framework.TestCase; + +import net.sf.acegisecurity.GrantedAuthority; +import net.sf.acegisecurity.GrantedAuthorityImpl; +import net.sf.acegisecurity.context.ContextHolder; +import net.sf.acegisecurity.context.SecureContextImpl; +import net.sf.acegisecurity.providers.TestingAuthenticationToken; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.tagext.Tag; + + +/** + * DOCUMENT ME! + * + * @author Francois Beausoleil + * @version $Id$ + */ +public class AuthorizeTagAttributeTests extends TestCase { + //~ Instance fields ======================================================== + + private final AuthorizeTag authorizeTag = new AuthorizeTag(); + private SecureContextImpl context; + private TestingAuthenticationToken currentUser; + + //~ Methods ================================================================ + + public void testAssertsIfAllGrantedSecond() throws JspException { + authorizeTag.setIfAllGranted("ROLE_SUPERVISOR,ROLE_SUPERTELLER"); + authorizeTag.setIfAnyGranted("ROLE_RESTRICTED"); + assertEquals("prevents request - principal is missing ROLE_SUPERTELLER", + Tag.SKIP_BODY, authorizeTag.doStartTag()); + } + + public void testAssertsIfAnyGrantedLast() throws JspException { + authorizeTag.setIfAnyGranted("ROLE_BANKER"); + assertEquals("prevents request - principal is missing ROLE_BANKER", + Tag.SKIP_BODY, authorizeTag.doStartTag()); + } + + public void testAssertsIfNotGrantedFirst() throws JspException { + authorizeTag.setIfNotGranted("ROLE_RESTRICTED"); + authorizeTag.setIfAllGranted("ROLE_SUPERVISOR,ROLE_RESTRICTED"); + authorizeTag.setIfAnyGranted("ROLE_SUPERVISOR"); + assertEquals("prevents request - principal has ROLE_RESTRICTED", + Tag.SKIP_BODY, authorizeTag.doStartTag()); + } + + protected void setUp() throws Exception { + super.setUp(); + + currentUser = new TestingAuthenticationToken("abc", "123", + new GrantedAuthority[] {new GrantedAuthorityImpl( + "ROLE_SUPERVISOR"), new GrantedAuthorityImpl( + "ROLE_RESTRICTED"),}); + + context = new SecureContextImpl(); + context.setAuthentication(currentUser); + + ContextHolder.setContext(context); + } +} diff --git a/core/src/test/java/org/acegisecurity/taglibs/authz/AuthorizeTagTests.java b/core/src/test/java/org/acegisecurity/taglibs/authz/AuthorizeTagTests.java new file mode 100644 index 0000000000..257889ee2e --- /dev/null +++ b/core/src/test/java/org/acegisecurity/taglibs/authz/AuthorizeTagTests.java @@ -0,0 +1,112 @@ +/* + * The Acegi Security System for Spring is published under the terms + * of the Apache Software License. + * + * Visit http://acegisecurity.sourceforge.net for further details. + */ + +package net.sf.acegisecurity.taglibs.authz; + +import junit.framework.TestCase; + +import net.sf.acegisecurity.GrantedAuthority; +import net.sf.acegisecurity.GrantedAuthorityImpl; +import net.sf.acegisecurity.context.ContextHolder; +import net.sf.acegisecurity.context.SecureContextImpl; +import net.sf.acegisecurity.providers.TestingAuthenticationToken; + +import javax.servlet.jsp.JspException; +import javax.servlet.jsp.tagext.Tag; + + +/** + * DOCUMENT ME! + * + * @author Francois Beausoleil + * @version $Id$ + */ +public class AuthorizeTagTests extends TestCase { + //~ Instance fields ======================================================== + + private final AuthorizeTag authorizeTag = new AuthorizeTag(); + private SecureContextImpl context; + private TestingAuthenticationToken currentUser; + + //~ Methods ================================================================ + + public void testDefaultsToNotOutputtingBodyWhenNoRequiredAuthorities() + throws JspException { + assertEquals("", authorizeTag.getIfAllGranted()); + assertEquals("", authorizeTag.getIfAnyGranted()); + assertEquals("", authorizeTag.getIfNotGranted()); + + assertEquals("prevents body output - no authorities granted", + Tag.SKIP_BODY, authorizeTag.doStartTag()); + } + + public void testOutputsBodyIfOneRolePresent() throws JspException { + authorizeTag.setIfAnyGranted("ROLE_TELLER"); + assertEquals("authorized - ROLE_TELLER in both sets", + Tag.EVAL_BODY_INCLUDE, authorizeTag.doStartTag()); + } + + public void testOutputsBodyWhenAllGranted() throws JspException { + authorizeTag.setIfAllGranted("ROLE_SUPERVISOR,ROLE_TELLER"); + assertEquals("allows request - all required roles granted on principal", + Tag.EVAL_BODY_INCLUDE, authorizeTag.doStartTag()); + } + + public void testOutputsBodyWhenNotGrantedSatisfied() + throws JspException { + authorizeTag.setIfNotGranted("ROLE_BANKER"); + assertEquals("allows request - principal doesn't have ROLE_BANKER", + Tag.EVAL_BODY_INCLUDE, authorizeTag.doStartTag()); + } + + public void testSkipsBodyIfNoAnyRolePresent() throws JspException { + authorizeTag.setIfAnyGranted("ROLE_BANKER"); + assertEquals("unauthorized - ROLE_BANKER not in granted authorities", + Tag.SKIP_BODY, authorizeTag.doStartTag()); + } + + public void testSkipsBodyWhenMissingAnAllGranted() + throws JspException { + authorizeTag.setIfAllGranted("ROLE_SUPERVISOR,ROLE_TELLER,ROLE_BANKER"); + assertEquals("prevents request - missing ROLE_BANKER on principal", + Tag.SKIP_BODY, authorizeTag.doStartTag()); + } + + public void testSkipsBodyWhenNotGrantedUnsatisfied() + throws JspException { + authorizeTag.setIfNotGranted("ROLE_TELLER"); + assertEquals("prevents request - principal has ROLE_TELLER", + Tag.SKIP_BODY, authorizeTag.doStartTag()); + } + + public void testUsesAllAuthoritiesToDetermineAccess() { + authorizeTag.setIfAllGranted("ROLE_SUPERVISOR,ROLE_BANKER"); + authorizeTag.setIfAnyGranted("ROLE_BANKER"); + authorizeTag.setIfNotGranted("ROLE_RESTRICTED"); + + currentUser = new TestingAuthenticationToken("abc", "123", + new GrantedAuthority[] {new GrantedAuthorityImpl( + "ROLE_SUPERVISOR"), new GrantedAuthorityImpl( + "ROLE_BANKER"), new GrantedAuthorityImpl( + "ROLE_RESTRICTED"),}); + context.setAuthentication(currentUser); + } + + protected void setUp() throws Exception { + super.setUp(); + + currentUser = new TestingAuthenticationToken("abc", "123", + new GrantedAuthority[] {new GrantedAuthorityImpl( + "ROLE_SUPERVISOR"), new GrantedAuthorityImpl( + "ROLE_TELLER"),}); + + context = new SecureContextImpl(); + context.setAuthentication(currentUser); + + ContextHolder.setContext(context); + } +} diff --git a/project.properties b/project.properties index 0f3d1d39e8..f5d3108f4a 100644 --- a/project.properties +++ b/project.properties @@ -27,6 +27,9 @@ target.testclasses.dir=${target.dir}/test-classes # Names of distribution jar files acegi-security.jar=acegi-security.jar +# Names of distribution jar files +acegi-taglib.jar=acegi-security-taglib.jar + # Name of Zip file containing all project sources acegi-security-src.zip=acegi-security-src.zip