SEC-717: Resolve UserDetails.getAuthorities() sort logic issue.

This commit is contained in:
Ben Alex 2008-03-16 04:02:55 +00:00
parent 820c529809
commit 6bc0585e4a
6 changed files with 42 additions and 9 deletions

View File

@ -17,6 +17,8 @@ package org.springframework.security;
import java.io.Serializable; import java.io.Serializable;
import org.springframework.security.userdetails.UserDetails;
/** /**
* Represents an authority granted to an {@link Authentication} object. * Represents an authority granted to an {@link Authentication} object.
* *
@ -26,10 +28,16 @@ import java.io.Serializable;
* AccessDecisionManager}. * AccessDecisionManager}.
* </p> * </p>
* *
* <p>
* Implementations must implement {@link Comparable} in order to ensure that
* array sorting logic guaranteed by {@link UserDetails#getAuthorities()} can
* be reliably implemented.
* </p>
*
* @author Ben Alex * @author Ben Alex
* @version $Id$ * @version $Id$
*/ */
public interface GrantedAuthority extends Serializable { public interface GrantedAuthority extends Serializable, Comparable {
//~ Methods ======================================================================================================== //~ Methods ========================================================================================================
/** /**

View File

@ -17,6 +17,8 @@ package org.springframework.security;
import java.io.Serializable; import java.io.Serializable;
import org.springframework.util.Assert;
/** /**
* Basic concrete implementation of a {@link GrantedAuthority}.<p>Stores a <code>String</code> representation of an * Basic concrete implementation of a {@link GrantedAuthority}.<p>Stores a <code>String</code> representation of an
@ -35,6 +37,7 @@ public class GrantedAuthorityImpl implements GrantedAuthority, Serializable {
public GrantedAuthorityImpl(String role) { public GrantedAuthorityImpl(String role) {
super(); super();
Assert.hasText(role, "A granted authority textual representation is required");
this.role = role; this.role = role;
} }
@ -65,4 +68,12 @@ public class GrantedAuthorityImpl implements GrantedAuthority, Serializable {
public String toString() { public String toString() {
return this.role; return this.role;
} }
public int compareTo(Object o) {
if (o != null && o instanceof GrantedAuthority) {
GrantedAuthority rhs = (GrantedAuthority) o;
return this.role.compareTo(rhs.getAuthority());
}
return -1;
}
} }

View File

@ -15,8 +15,11 @@
package org.springframework.security.userdetails; package org.springframework.security.userdetails;
import org.springframework.security.GrantedAuthority; import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;
import org.springframework.security.GrantedAuthority;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -231,13 +234,15 @@ public class User implements UserDetails {
protected void setAuthorities(GrantedAuthority[] authorities) { protected void setAuthorities(GrantedAuthority[] authorities) {
Assert.notNull(authorities, "Cannot pass a null GrantedAuthority array"); Assert.notNull(authorities, "Cannot pass a null GrantedAuthority array");
// Ensure array iteration order is predictable (as per UserDetails.getAuthorities() contract and SEC-xxx)
SortedSet sorter = new TreeSet();
for (int i = 0; i < authorities.length; i++) { for (int i = 0; i < authorities.length; i++) {
Assert.notNull(authorities[i], Assert.notNull(authorities[i],
"Granted authority element " + i + " is null - GrantedAuthority[] cannot contain any null elements"); "Granted authority element " + i + " is null - GrantedAuthority[] cannot contain any null elements");
sorter.add(authorities[i]);
} }
this.authorities = authorities; this.authorities = (GrantedAuthority[]) sorter.toArray(new GrantedAuthority[sorter.size()]);
} }
public String toString() { public String toString() {

View File

@ -54,7 +54,7 @@ public interface UserDetails extends Serializable {
/** /**
* Returns the authorities granted to the user. Cannot return <code>null</code>. * Returns the authorities granted to the user. Cannot return <code>null</code>.
* *
* @return the authorities (never <code>null</code>) * @return the authorities, sorted by natural key (never <code>null</code>)
*/ */
GrantedAuthority[] getAuthorities(); GrantedAuthority[] getAuthorities();

View File

@ -76,14 +76,18 @@ public class GrantedAuthorityImplTests extends TestCase {
//~ Inner Classes ================================================================================================== //~ Inner Classes ==================================================================================================
private class MockGrantedAuthorityImpl implements GrantedAuthority { private class MockGrantedAuthorityImpl implements GrantedAuthority, Comparable {
private String role; private String role;
public MockGrantedAuthorityImpl(String role) { public MockGrantedAuthorityImpl(String role) {
this.role = role; this.role = role;
} }
private MockGrantedAuthorityImpl() { public int compareTo(Object o) {
return this.role.compareTo(((GrantedAuthority)o).getAuthority());
}
private MockGrantedAuthorityImpl() {
super(); super();
} }

View File

@ -64,6 +64,11 @@ public class UserTests extends TestCase {
new User("rod", "koala", true, true, true, true, new User("rod", "koala", true, true, true, true,
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO")}))); new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO")})));
// Equal as the new User will internally sort the GrantedAuthorities in the correct order, before running equals()
assertTrue(user1.equals(
new User("rod", "koala", true, true, true, true,
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_TWO"), new GrantedAuthorityImpl("ROLE_ONE")})));
assertFalse(user1.equals( assertFalse(user1.equals(
new User("DIFFERENT_USERNAME", "koala", true, true, true, true, new User("DIFFERENT_USERNAME", "koala", true, true, true, true,
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO")}))); new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO")})));
@ -153,7 +158,7 @@ public class UserTests extends TestCase {
public void testUserGettersSetter() throws Exception { public void testUserGettersSetter() throws Exception {
UserDetails user = new User("rod", "koala", true, true, true, true, UserDetails user = new User("rod", "koala", true, true, true, true,
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl("ROLE_TWO")}); new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_TWO"), new GrantedAuthorityImpl("ROLE_ONE")});
assertEquals("rod", user.getUsername()); assertEquals("rod", user.getUsername());
assertEquals("koala", user.getPassword()); assertEquals("koala", user.getPassword());
assertTrue(user.isEnabled()); assertTrue(user.isEnabled());