diff --git a/core/src/test/java/org/springframework/security/core/userdetails/memory/UserMapTests.java b/core/src/test/java/org/springframework/security/core/userdetails/memory/UserMapTests.java index 08213b761e..581c1db957 100644 --- a/core/src/test/java/org/springframework/security/core/userdetails/memory/UserMapTests.java +++ b/core/src/test/java/org/springframework/security/core/userdetails/memory/UserMapTests.java @@ -15,8 +15,10 @@ package org.springframework.security.core.userdetails.memory; +import static org.junit.Assert.*; import junit.framework.TestCase; +import org.junit.Test; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; @@ -29,9 +31,9 @@ import org.springframework.security.core.userdetails.memory.UserMap; * * @author Ben Alex */ -public class UserMapTests extends TestCase { +public class UserMapTests { - //~ Methods ======================================================================================================== + @Test public void testAddAndRetrieveUser() { UserDetails rod = new User("rod", "koala", true, true, true, true, AuthorityUtils.createAuthorityList("ROLE_ONE","ROLE_TWO")); @@ -50,7 +52,8 @@ public class UserMapTests extends TestCase { assertEquals(peter, map.getUser("peter")); } - public void testNullUserCannotBeAdded() { + @Test + public void nullUserCannotBeAdded() { UserMap map = new UserMap(); assertEquals(0, map.getUserCount()); @@ -62,7 +65,8 @@ public class UserMapTests extends TestCase { } } - public void testUnknownUserIsNotRetrieved() { + @Test + public void unknownUserIsNotRetrieved() { UserDetails rod = new User("rod", "koala", true, true, true, true, AuthorityUtils.createAuthorityList("ROLE_ONE","ROLE_TWO")); UserMap map = new UserMap(); diff --git a/itest/web/src/main/webapp/WEB-INF/in-memory-provider.xml b/itest/web/src/main/webapp/WEB-INF/in-memory-provider.xml index 83096639be..1f08e2c585 100644 --- a/itest/web/src/main/webapp/WEB-INF/in-memory-provider.xml +++ b/itest/web/src/main/webapp/WEB-INF/in-memory-provider.xml @@ -12,6 +12,7 @@ + diff --git a/itest/web/src/main/webapp/WEB-INF/security.tld b/itest/web/src/main/webapp/WEB-INF/security.tld new file mode 100644 index 0000000000..78ce820d1e --- /dev/null +++ b/itest/web/src/main/webapp/WEB-INF/security.tld @@ -0,0 +1,158 @@ + + + + 1.0 + 1.2 + security + http://www.springframework.org/security/tags + + Spring Security Authorization Tag Library + $Id$ + + + + authorize + org.springframework.security.taglibs.authz.AuthorizeTag + + A tag which outputs the body of the tag if the configured access expression + evaluates to true for the currently authenticated principal. + + + + access + false + false + + A Spring-EL expression which is supported by the WebSecurityExpressionHandler + in the application context. The latter will be used to evaluate the expression. + + + + + url + false + false + + A URL within the application. If the user has access to this URL (as determined by + the AccessDecisionManager), the tag body will be evaluated. If not, it will + be skipped. + + + + + method + false + false + + Can optionally be used to narrow down the HTTP method (typically GET or POST) to which the URL + applies to. Only has any meaning when used in combination with the "url" attribute. + + + + + + ifNotGranted + false + true + + A comma separated list of roles which the user must not have + for the body to be output. Deprecated in favour of the access expression. + + + + + ifAllGranted + false + true + + A comma separated list of roles which the user must all + possess for the body to be output. Deprecated in favour of the access expression. + + + + + ifAnyGranted + false + true + + A comma separated list of roles, one of which the user must + possess for the body to be output. Deprecated in favour of the access expression. + + + + + + authentication + org.springframework.security.taglibs.authz.AuthenticationTag + + Allows access to the current Authentication object. + + + + property + true + true + + Property of the Authentication object which should be output. Supports nested + properties. For example if the principal object is an instance of UserDetails, + the property "principal.username" will return the username. Alternatively, using + "name" will call getName method on the Authentication object directly. + + + + var + false + false + + Name of the exported scoped variable which will contain the + evaluated property of the Authentication object. + + + + Set HTML escaping for this tag, as a boolean value. + htmlEscape + false + true + + + scope + false + false + + Scope for var. + + + + + + accesscontrollist + org.springframework.security.taglibs.authz.AccessControlListTag + + Allows inclusion of a tag body if the current Authentication + has one of the specified permissions to the presented + domain object instance. + + + + hasPermission + true + true + + A comma separated list of permissions, which will be converted to + Permission instances by the configured PermissionFactory. + + + + domainObject + true + true + + The actual domain object instance for which permissions + are being evaluated. + + + + + diff --git a/itest/web/src/test/java/org/springframework/security/integration/InMemoryProviderWebAppTests.java b/itest/web/src/test/java/org/springframework/security/integration/InMemoryProviderWebAppTests.java index bbeaab393a..7d6311008b 100644 --- a/itest/web/src/test/java/org/springframework/security/integration/InMemoryProviderWebAppTests.java +++ b/itest/web/src/test/java/org/springframework/security/integration/InMemoryProviderWebAppTests.java @@ -95,4 +95,16 @@ public class InMemoryProviderWebAppTests extends AbstractWebServerIntegrationTes 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<>&.")); + assertTrue(response.contains("This is the principal.username: theescapist<>&.")); + } + + } diff --git a/taglibs/src/main/java/org/springframework/security/taglibs/authz/AuthenticationTag.java b/taglibs/src/main/java/org/springframework/security/taglibs/authz/AuthenticationTag.java index efe8f5ca3a..04a286be29 100644 --- a/taglibs/src/main/java/org/springframework/security/taglibs/authz/AuthenticationTag.java +++ b/taglibs/src/main/java/org/springframework/security/taglibs/authz/AuthenticationTag.java @@ -23,6 +23,7 @@ import org.springframework.security.web.util.TextEscapeUtils; import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.BeansException; +import org.springframework.web.util.ExpressionEvaluationUtils; import org.springframework.web.util.TagUtils; import java.io.IOException; @@ -48,6 +49,7 @@ public class AuthenticationTag extends TagSupport { private String property; private int scope; private boolean scopeSpecified; + private boolean htmlEscape = true; //~ Methods ======================================================================================================== @@ -120,7 +122,11 @@ public class AuthenticationTag extends TagSupport { } } } else { - writeMessage(TextEscapeUtils.escapeEntities(String.valueOf(result))); + if (htmlEscape) { + writeMessage(TextEscapeUtils.escapeEntities(String.valueOf(result))); + } else { + writeMessage(String.valueOf(result)); + } } return EVAL_PAGE; } @@ -132,4 +138,21 @@ public class AuthenticationTag extends TagSupport { throw new JspException(ioe); } } + + + /** + * Set HTML escaping for this tag, as boolean value. + */ + public void setHtmlEscape(String htmlEscape) throws JspException { + this.htmlEscape = ExpressionEvaluationUtils.evaluateBoolean("htmlEscape", htmlEscape, pageContext); + } + + /** + * Return the HTML escaping setting for this tag, + * or the default setting if not overridden. + * @see #isDefaultHtmlEscape() + */ + protected boolean isHtmlEscape() { + return htmlEscape; + } } diff --git a/taglibs/src/main/resources/META-INF/security.tld b/taglibs/src/main/resources/META-INF/security.tld index 410ee656be..78ce820d1e 100644 --- a/taglibs/src/main/resources/META-INF/security.tld +++ b/taglibs/src/main/resources/META-INF/security.tld @@ -110,6 +110,12 @@ evaluated property of the Authentication object. + + Set HTML escaping for this tag, as a boolean value. + htmlEscape + false + true + scope false diff --git a/taglibs/src/test/java/org/springframework/security/taglibs/authz/AuthenticationTagTests.java b/taglibs/src/test/java/org/springframework/security/taglibs/authz/AuthenticationTagTests.java index 201a27ceb3..518a4ba188 100644 --- a/taglibs/src/test/java/org/springframework/security/taglibs/authz/AuthenticationTagTests.java +++ b/taglibs/src/test/java/org/springframework/security/taglibs/authz/AuthenticationTagTests.java @@ -15,11 +15,13 @@ package org.springframework.security.taglibs.authz; +import static org.junit.Assert.*; + import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.Tag; -import junit.framework.TestCase; - +import org.junit.After; +import org.junit.Test; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.AuthorityUtils; @@ -32,7 +34,7 @@ import org.springframework.security.core.userdetails.User; * * @author Ben Alex */ -public class AuthenticationTagTests extends TestCase { +public class AuthenticationTagTests { //~ Instance fields ================================================================================================ private final MyAuthenticationTag authenticationTag = new MyAuthenticationTag(); @@ -41,10 +43,12 @@ public class AuthenticationTagTests extends TestCase { //~ Methods ======================================================================================================== - protected void tearDown() throws Exception { + @After + public void tearDown() { SecurityContextHolder.clearContext(); } + @Test public void testOperationWhenPrincipalIsAUserDetailsInstance()throws JspException { SecurityContextHolder.getContext().setAuthentication(auth); @@ -54,6 +58,7 @@ public class AuthenticationTagTests extends TestCase { assertEquals("rodUserDetails", authenticationTag.getLastMessage()); } + @Test public void testOperationWhenPrincipalIsAString() throws JspException { SecurityContextHolder.getContext().setAuthentication( new TestingAuthenticationToken("rodAsString", "koala", AuthorityUtils.NO_AUTHORITIES )); @@ -64,6 +69,7 @@ public class AuthenticationTagTests extends TestCase { assertEquals("rodAsString", authenticationTag.getLastMessage()); } + @Test public void testNestedPropertyIsReadCorrectly() throws JspException { SecurityContextHolder.getContext().setAuthentication(auth); @@ -73,6 +79,7 @@ public class AuthenticationTagTests extends TestCase { assertEquals("rodUserDetails", authenticationTag.getLastMessage()); } + @Test public void testOperationWhenPrincipalIsNull() throws JspException { SecurityContextHolder.getContext().setAuthentication( new TestingAuthenticationToken(null, "koala", AuthorityUtils.NO_AUTHORITIES )); @@ -82,6 +89,7 @@ public class AuthenticationTagTests extends TestCase { assertEquals(Tag.EVAL_PAGE, authenticationTag.doEndTag()); } + @Test public void testOperationWhenSecurityContextIsNull() throws Exception { SecurityContextHolder.getContext().setAuthentication(null); @@ -91,12 +99,14 @@ public class AuthenticationTagTests extends TestCase { assertEquals(null, authenticationTag.getLastMessage()); } + @Test public void testSkipsBodyIfNullOrEmptyOperation() throws Exception { authenticationTag.setProperty(""); assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag()); assertEquals(Tag.EVAL_PAGE, authenticationTag.doEndTag()); } + @Test public void testThrowsExceptionForUnrecognisedProperty() { SecurityContextHolder.getContext().setAuthentication(auth); authenticationTag.setProperty("qsq"); @@ -109,6 +119,25 @@ public class AuthenticationTagTests extends TestCase { } } + @Test + public void htmlEscapingIsUsedByDefault() throws Exception { + SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("<>& ", "")); + authenticationTag.setProperty("name"); + authenticationTag.doStartTag(); + authenticationTag.doEndTag(); + assertEquals("<>& ", authenticationTag.getLastMessage()); + } + + @Test + public void settingHtmlEscapeToFalsePreventsEscaping() throws Exception { + SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("<>& ", "")); + authenticationTag.setProperty("name"); + authenticationTag.setHtmlEscape("false"); + authenticationTag.doStartTag(); + authenticationTag.doEndTag(); + assertEquals("<>& ", authenticationTag.getLastMessage()); + } + //~ Inner Classes ================================================================================================== private class MyAuthenticationTag extends AuthenticationTag {