SEC-1304: Remove Comparable interface from GrantedAuthority to enable it to be imlemented by an enum.

This commit is contained in:
Luke Taylor 2009-11-30 21:22:11 +00:00
parent 1df82654e3
commit e72cfd58d4
2 changed files with 38 additions and 30 deletions

View File

@ -18,7 +18,6 @@ package org.springframework.security.core;
import java.io.Serializable; import java.io.Serializable;
import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.core.userdetails.UserDetails;
/** /**
* Represents an authority granted to an {@link Authentication} object. * Represents an authority granted to an {@link Authentication} object.
@ -27,10 +26,6 @@ import org.springframework.security.core.userdetails.UserDetails;
* A <code>GrantedAuthority</code> must either represent itself as a * A <code>GrantedAuthority</code> must either represent itself as a
* <code>String</code> or be specifically supported by an {@link * <code>String</code> or be specifically supported by an {@link
* AccessDecisionManager}. * AccessDecisionManager}.
* <p>
* Implementations must implement {@link Comparable} in order to ensure that
* array sorting logic guaranteed by {@link UserDetails#getAuthorities()} can
* be reliably implemented.
* *
* @author Ben Alex * @author Ben Alex
* @version $Id$ * @version $Id$
@ -41,11 +36,12 @@ public interface GrantedAuthority extends Serializable, Comparable<GrantedAuthor
/** /**
* If the <code>GrantedAuthority</code> can be represented as a <code>String</code> and that * If the <code>GrantedAuthority</code> can be represented as a <code>String</code> and that
* <code>String</code> is sufficient in precision to be relied upon for an access control decision by an {@link * <code>String</code> is sufficient in precision to be relied upon for an access control decision by an {@link
* AccessDecisionManager} (or delegate), this method should return such a <code>String</code>.<p>If the * AccessDecisionManager} (or delegate), this method should return such a <code>String</code>.
* <code>GrantedAuthority</code> cannot be expressed with sufficient precision as a <code>String</code>, * <p>
* If the <code>GrantedAuthority</code> cannot be expressed with sufficient precision as a <code>String</code>,
* <code>null</code> should be returned. Returning <code>null</code> will require an * <code>null</code> should be returned. Returning <code>null</code> will require an
* <code>AccessDecisionManager</code> (or delegate) to specifically support the <code>GrantedAuthority</code> * <code>AccessDecisionManager</code> (or delegate) to specifically support the <code>GrantedAuthority</code>
* implementation, so returning <code>null</code> should be avoided unless actually required.</p> * implementation, so returning <code>null</code> should be avoided unless actually required.
* *
* @return a representation of the granted authority (or <code>null</code> if the granted authority cannot be * @return a representation of the granted authority (or <code>null</code> if the granted authority cannot be
* expressed as a <code>String</code> with sufficient precision). * expressed as a <code>String</code> with sufficient precision).

View File

@ -15,11 +15,11 @@
package org.springframework.security.core.userdetails; package org.springframework.security.core.userdetails;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.Comparator;
import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
@ -40,7 +40,7 @@ public class User implements UserDetails {
//~ Instance fields ================================================================================================ //~ Instance fields ================================================================================================
private final String password; private final String password;
private final String username; private final String username;
private final List<GrantedAuthority> authorities; private final Set<GrantedAuthority> authorities;
private final boolean accountNonExpired; private final boolean accountNonExpired;
private final boolean accountNonLocked; private final boolean accountNonLocked;
private final boolean credentialsNonExpired; private final boolean credentialsNonExpired;
@ -78,7 +78,7 @@ public class User implements UserDetails {
* *
* @throws IllegalArgumentException if a <code>null</code> value was passed * @throws IllegalArgumentException if a <code>null</code> value was passed
* either as a parameter or as an element in the * either as a parameter or as an element in the
* <code>GrantedAuthority[]</code> array * <code>GrantedAuthority</code> collection
*/ */
public User(String username, String password, boolean enabled, boolean accountNonExpired, public User(String username, String password, boolean enabled, boolean accountNonExpired,
boolean credentialsNonExpired, boolean accountNonLocked, Collection<GrantedAuthority> authorities) { boolean credentialsNonExpired, boolean accountNonLocked, Collection<GrantedAuthority> authorities) {
@ -93,7 +93,7 @@ public class User implements UserDetails {
this.accountNonExpired = accountNonExpired; this.accountNonExpired = accountNonExpired;
this.credentialsNonExpired = credentialsNonExpired; this.credentialsNonExpired = credentialsNonExpired;
this.accountNonLocked = accountNonLocked; this.accountNonLocked = accountNonLocked;
this.authorities = Collections.unmodifiableList(sortAuthorities(authorities)); this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
} }
//~ Methods ======================================================================================================== //~ Methods ========================================================================================================
@ -105,7 +105,7 @@ public class User implements UserDetails {
User user = (User) rhs; User user = (User) rhs;
// We rely on constructor to guarantee any User has non-null and >0 // We rely on constructor to guarantee any User has non-null
// authorities // authorities
if (!authorities.equals(user.authorities)) { if (!authorities.equals(user.authorities)) {
return false; return false;
@ -134,10 +134,8 @@ public class User implements UserDetails {
public int hashCode() { public int hashCode() {
int code = 9792; int code = 9792;
if (this.getAuthorities() != null) { for (GrantedAuthority authority : getAuthorities()) {
for (int i = 0; i < this.getAuthorities().size(); i++) { code = code * (authority.hashCode() % 7);
code = code * (authorities.get(i).hashCode() % 7);
}
} }
if (this.getPassword() != null) { if (this.getPassword() != null) {
@ -183,19 +181,31 @@ public class User implements UserDetails {
return enabled; return enabled;
} }
private static List<GrantedAuthority> sortAuthorities(Collection<GrantedAuthority> authorities) { private static SortedSet<GrantedAuthority> sortAuthorities(Collection<GrantedAuthority> authorities) {
Assert.notNull(authorities, "Cannot pass a null GrantedAuthority array"); Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection");
// Ensure array iteration order is predictable (as per UserDetails.getAuthorities() contract and SEC-xxx) // Ensure array iteration order is predictable (as per UserDetails.getAuthorities() contract and SEC-717)
SortedSet<GrantedAuthority> sorter = new TreeSet<GrantedAuthority>(); SortedSet<GrantedAuthority> sortedAuthorities =
new TreeSet<GrantedAuthority>(new Comparator<GrantedAuthority>() {
public int compare(GrantedAuthority g1, GrantedAuthority g2) {
// Neither should ever be null as each entry is checked before adding it to the set.
// If the authority is null, it is a custom authority and should precede others.
if (g2.getAuthority() == null) {
return -1;
}
if (g1.getAuthority() == null) {
return 1;
}
return g1.getAuthority().compareTo(g2.getAuthority());
}
});
for (GrantedAuthority grantedAuthority : authorities) { for (GrantedAuthority grantedAuthority : authorities) {
Assert.notNull(grantedAuthority, "GrantedAuthority list cannot contain any null elements"); Assert.notNull(grantedAuthority, "GrantedAuthority list cannot contain any null elements");
sorter.add(grantedAuthority); sortedAuthorities.add(grantedAuthority);
} }
List<GrantedAuthority> sortedAuthorities = new ArrayList<GrantedAuthority>(sorter.size());
sortedAuthorities.addAll(sorter);
return sortedAuthorities; return sortedAuthorities;
} }
@ -212,12 +222,14 @@ public class User implements UserDetails {
if (this.getAuthorities() != null) { if (this.getAuthorities() != null) {
sb.append("Granted Authorities: "); sb.append("Granted Authorities: ");
for (int i = 0; i < authorities.size(); i++) { boolean first = true;
if (i > 0) { for (GrantedAuthority auth : authorities) {
if (!first) {
sb.append(", "); sb.append(", ");
first = false;
} }
sb.append(authorities.get(i)); sb.append(auth);
} }
} else { } else {
sb.append("Not granted any authorities"); sb.append("Not granted any authorities");