SEC-1420: Add htmlEscape attribute to authentication JSP tag.
This allows HTML escaping to be disabled if required.
This commit is contained in:
parent
43f3568b16
commit
0551dd89ac
|
@ -15,8 +15,10 @@
|
||||||
|
|
||||||
package org.springframework.security.core.userdetails.memory;
|
package org.springframework.security.core.userdetails.memory;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
import org.springframework.security.core.userdetails.User;
|
import org.springframework.security.core.userdetails.User;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
@ -29,9 +31,9 @@ import org.springframework.security.core.userdetails.memory.UserMap;
|
||||||
*
|
*
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
*/
|
*/
|
||||||
public class UserMapTests extends TestCase {
|
public class UserMapTests {
|
||||||
|
|
||||||
//~ Methods ========================================================================================================
|
@Test
|
||||||
public void testAddAndRetrieveUser() {
|
public void testAddAndRetrieveUser() {
|
||||||
UserDetails rod = new User("rod", "koala", true, true, true, true,
|
UserDetails rod = new User("rod", "koala", true, true, true, true,
|
||||||
AuthorityUtils.createAuthorityList("ROLE_ONE","ROLE_TWO"));
|
AuthorityUtils.createAuthorityList("ROLE_ONE","ROLE_TWO"));
|
||||||
|
@ -50,7 +52,8 @@ public class UserMapTests extends TestCase {
|
||||||
assertEquals(peter, map.getUser("peter"));
|
assertEquals(peter, map.getUser("peter"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNullUserCannotBeAdded() {
|
@Test
|
||||||
|
public void nullUserCannotBeAdded() {
|
||||||
UserMap map = new UserMap();
|
UserMap map = new UserMap();
|
||||||
assertEquals(0, map.getUserCount());
|
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,
|
UserDetails rod = new User("rod", "koala", true, true, true, true,
|
||||||
AuthorityUtils.createAuthorityList("ROLE_ONE","ROLE_TWO"));
|
AuthorityUtils.createAuthorityList("ROLE_ONE","ROLE_TWO"));
|
||||||
UserMap map = new UserMap();
|
UserMap map = new UserMap();
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
<user name="miles" password="milespassword" authorities="ROLE_USER,ROLE_JAZZ,ROLE_TRUMPETER"/>
|
<user name="miles" password="milespassword" authorities="ROLE_USER,ROLE_JAZZ,ROLE_TRUMPETER"/>
|
||||||
<user name="johnc" password="johncspassword" authorities="ROLE_USER,ROLE_JAZZ,ROLE_SAXOPHONIST"/>
|
<user name="johnc" password="johncspassword" authorities="ROLE_USER,ROLE_JAZZ,ROLE_SAXOPHONIST"/>
|
||||||
<user name="jimi" password="jimispassword" authorities="ROLE_USER,ROLE_ROCK,ROLE_GUITARIST"/>
|
<user name="jimi" password="jimispassword" authorities="ROLE_USER,ROLE_ROCK,ROLE_GUITARIST"/>
|
||||||
|
<user name="theescapist<>&." password="theescapistspassword" authorities="ROLE_USER"/>
|
||||||
</user-service>
|
</user-service>
|
||||||
</authentication-provider>
|
</authentication-provider>
|
||||||
</authentication-manager>
|
</authentication-manager>
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||||
|
<!DOCTYPE taglib
|
||||||
|
PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
|
||||||
|
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
|
||||||
|
<taglib>
|
||||||
|
<tlib-version>1.0</tlib-version>
|
||||||
|
<jsp-version>1.2</jsp-version>
|
||||||
|
<short-name>security</short-name>
|
||||||
|
<uri>http://www.springframework.org/security/tags</uri>
|
||||||
|
<description>
|
||||||
|
Spring Security Authorization Tag Library
|
||||||
|
$Id$
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<tag>
|
||||||
|
<name>authorize</name>
|
||||||
|
<tag-class>org.springframework.security.taglibs.authz.AuthorizeTag</tag-class>
|
||||||
|
<description>
|
||||||
|
A tag which outputs the body of the tag if the configured access expression
|
||||||
|
evaluates to true for the currently authenticated principal.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>access</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>false</rtexprvalue>
|
||||||
|
<description>
|
||||||
|
A Spring-EL expression which is supported by the WebSecurityExpressionHandler
|
||||||
|
in the application context. The latter will be used to evaluate the expression.
|
||||||
|
</description>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>url</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>false</rtexprvalue>
|
||||||
|
<description>
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>method</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>false</rtexprvalue>
|
||||||
|
<description>
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>ifNotGranted</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>true</rtexprvalue>
|
||||||
|
<description>
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>ifAllGranted</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>true</rtexprvalue>
|
||||||
|
<description>
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>ifAnyGranted</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>true</rtexprvalue>
|
||||||
|
<description>
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
</attribute>
|
||||||
|
</tag>
|
||||||
|
|
||||||
|
<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>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,
|
||||||
|
the 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 which will contain the
|
||||||
|
evaluated property of the Authentication object.
|
||||||
|
</description>
|
||||||
|
</attribute>
|
||||||
|
<attribute>
|
||||||
|
<description>Set HTML escaping for this tag, as a boolean value.</description>
|
||||||
|
<name>htmlEscape</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>true</rtexprvalue>
|
||||||
|
</attribute>
|
||||||
|
<attribute>
|
||||||
|
<name>scope</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>false</rtexprvalue>
|
||||||
|
<description>
|
||||||
|
Scope for var.
|
||||||
|
</description>
|
||||||
|
</attribute>
|
||||||
|
</tag>
|
||||||
|
|
||||||
|
<tag>
|
||||||
|
<name>accesscontrollist</name>
|
||||||
|
<tag-class>org.springframework.security.taglibs.authz.AccessControlListTag</tag-class>
|
||||||
|
<description>
|
||||||
|
Allows inclusion of a tag body if the current Authentication
|
||||||
|
has one of the specified permissions to the presented
|
||||||
|
domain object instance.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>hasPermission</name>
|
||||||
|
<required>true</required>
|
||||||
|
<rtexprvalue>true</rtexprvalue>
|
||||||
|
<description>
|
||||||
|
A comma separated list of permissions, which will be converted to
|
||||||
|
Permission instances by the configured PermissionFactory.
|
||||||
|
</description>
|
||||||
|
</attribute>
|
||||||
|
<attribute>
|
||||||
|
<name>domainObject</name>
|
||||||
|
<required>true</required>
|
||||||
|
<rtexprvalue>true</rtexprvalue>
|
||||||
|
<description>
|
||||||
|
The actual domain object instance for which permissions
|
||||||
|
are being evaluated.
|
||||||
|
</description>
|
||||||
|
</attribute>
|
||||||
|
</tag>
|
||||||
|
|
||||||
|
</taglib>
|
|
@ -95,4 +95,16 @@ public class InMemoryProviderWebAppTests extends AbstractWebServerIntegrationTes
|
||||||
tester.assertTextPresent("This session has been expired");
|
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<>&."));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.springframework.security.web.util.TextEscapeUtils;
|
||||||
|
|
||||||
import org.springframework.beans.BeanWrapperImpl;
|
import org.springframework.beans.BeanWrapperImpl;
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.web.util.ExpressionEvaluationUtils;
|
||||||
import org.springframework.web.util.TagUtils;
|
import org.springframework.web.util.TagUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -48,6 +49,7 @@ public class AuthenticationTag extends TagSupport {
|
||||||
private String property;
|
private String property;
|
||||||
private int scope;
|
private int scope;
|
||||||
private boolean scopeSpecified;
|
private boolean scopeSpecified;
|
||||||
|
private boolean htmlEscape = true;
|
||||||
|
|
||||||
|
|
||||||
//~ Methods ========================================================================================================
|
//~ Methods ========================================================================================================
|
||||||
|
@ -120,7 +122,11 @@ public class AuthenticationTag extends TagSupport {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (htmlEscape) {
|
||||||
writeMessage(TextEscapeUtils.escapeEntities(String.valueOf(result)));
|
writeMessage(TextEscapeUtils.escapeEntities(String.valueOf(result)));
|
||||||
|
} else {
|
||||||
|
writeMessage(String.valueOf(result));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return EVAL_PAGE;
|
return EVAL_PAGE;
|
||||||
}
|
}
|
||||||
|
@ -132,4 +138,21 @@ public class AuthenticationTag extends TagSupport {
|
||||||
throw new JspException(ioe);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,6 +110,12 @@
|
||||||
evaluated property of the Authentication object.
|
evaluated property of the Authentication object.
|
||||||
</description>
|
</description>
|
||||||
</attribute>
|
</attribute>
|
||||||
|
<attribute>
|
||||||
|
<description>Set HTML escaping for this tag, as a boolean value.</description>
|
||||||
|
<name>htmlEscape</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>true</rtexprvalue>
|
||||||
|
</attribute>
|
||||||
<attribute>
|
<attribute>
|
||||||
<name>scope</name>
|
<name>scope</name>
|
||||||
<required>false</required>
|
<required>false</required>
|
||||||
|
|
|
@ -15,11 +15,13 @@
|
||||||
|
|
||||||
package org.springframework.security.taglibs.authz;
|
package org.springframework.security.taglibs.authz;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import javax.servlet.jsp.JspException;
|
import javax.servlet.jsp.JspException;
|
||||||
import javax.servlet.jsp.tagext.Tag;
|
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.authentication.TestingAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
|
@ -32,7 +34,7 @@ import org.springframework.security.core.userdetails.User;
|
||||||
*
|
*
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
*/
|
*/
|
||||||
public class AuthenticationTagTests extends TestCase {
|
public class AuthenticationTagTests {
|
||||||
//~ Instance fields ================================================================================================
|
//~ Instance fields ================================================================================================
|
||||||
|
|
||||||
private final MyAuthenticationTag authenticationTag = new MyAuthenticationTag();
|
private final MyAuthenticationTag authenticationTag = new MyAuthenticationTag();
|
||||||
|
@ -41,10 +43,12 @@ public class AuthenticationTagTests extends TestCase {
|
||||||
|
|
||||||
//~ Methods ========================================================================================================
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
protected void tearDown() throws Exception {
|
@After
|
||||||
|
public void tearDown() {
|
||||||
SecurityContextHolder.clearContext();
|
SecurityContextHolder.clearContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testOperationWhenPrincipalIsAUserDetailsInstance()throws JspException {
|
public void testOperationWhenPrincipalIsAUserDetailsInstance()throws JspException {
|
||||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||||
|
|
||||||
|
@ -54,6 +58,7 @@ public class AuthenticationTagTests extends TestCase {
|
||||||
assertEquals("rodUserDetails", authenticationTag.getLastMessage());
|
assertEquals("rodUserDetails", authenticationTag.getLastMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testOperationWhenPrincipalIsAString() throws JspException {
|
public void testOperationWhenPrincipalIsAString() throws JspException {
|
||||||
SecurityContextHolder.getContext().setAuthentication(
|
SecurityContextHolder.getContext().setAuthentication(
|
||||||
new TestingAuthenticationToken("rodAsString", "koala", AuthorityUtils.NO_AUTHORITIES ));
|
new TestingAuthenticationToken("rodAsString", "koala", AuthorityUtils.NO_AUTHORITIES ));
|
||||||
|
@ -64,6 +69,7 @@ public class AuthenticationTagTests extends TestCase {
|
||||||
assertEquals("rodAsString", authenticationTag.getLastMessage());
|
assertEquals("rodAsString", authenticationTag.getLastMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testNestedPropertyIsReadCorrectly() throws JspException {
|
public void testNestedPropertyIsReadCorrectly() throws JspException {
|
||||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||||
|
|
||||||
|
@ -73,6 +79,7 @@ public class AuthenticationTagTests extends TestCase {
|
||||||
assertEquals("rodUserDetails", authenticationTag.getLastMessage());
|
assertEquals("rodUserDetails", authenticationTag.getLastMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testOperationWhenPrincipalIsNull() throws JspException {
|
public void testOperationWhenPrincipalIsNull() throws JspException {
|
||||||
SecurityContextHolder.getContext().setAuthentication(
|
SecurityContextHolder.getContext().setAuthentication(
|
||||||
new TestingAuthenticationToken(null, "koala", AuthorityUtils.NO_AUTHORITIES ));
|
new TestingAuthenticationToken(null, "koala", AuthorityUtils.NO_AUTHORITIES ));
|
||||||
|
@ -82,6 +89,7 @@ public class AuthenticationTagTests extends TestCase {
|
||||||
assertEquals(Tag.EVAL_PAGE, authenticationTag.doEndTag());
|
assertEquals(Tag.EVAL_PAGE, authenticationTag.doEndTag());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testOperationWhenSecurityContextIsNull() throws Exception {
|
public void testOperationWhenSecurityContextIsNull() throws Exception {
|
||||||
SecurityContextHolder.getContext().setAuthentication(null);
|
SecurityContextHolder.getContext().setAuthentication(null);
|
||||||
|
|
||||||
|
@ -91,12 +99,14 @@ public class AuthenticationTagTests extends TestCase {
|
||||||
assertEquals(null, authenticationTag.getLastMessage());
|
assertEquals(null, authenticationTag.getLastMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSkipsBodyIfNullOrEmptyOperation() throws Exception {
|
public void testSkipsBodyIfNullOrEmptyOperation() throws Exception {
|
||||||
authenticationTag.setProperty("");
|
authenticationTag.setProperty("");
|
||||||
assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
|
assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
|
||||||
assertEquals(Tag.EVAL_PAGE, authenticationTag.doEndTag());
|
assertEquals(Tag.EVAL_PAGE, authenticationTag.doEndTag());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testThrowsExceptionForUnrecognisedProperty() {
|
public void testThrowsExceptionForUnrecognisedProperty() {
|
||||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||||
authenticationTag.setProperty("qsq");
|
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 ==================================================================================================
|
//~ Inner Classes ==================================================================================================
|
||||||
|
|
||||||
private class MyAuthenticationTag extends AuthenticationTag {
|
private class MyAuthenticationTag extends AuthenticationTag {
|
||||||
|
|
Loading…
Reference in New Issue