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 org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.core.userdetails.UserDetails;
/**
* 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
* <code>String</code> or be specifically supported by an {@link
* 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
* @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
* <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
* <code>GrantedAuthority</code> cannot be expressed with sufficient precision as a <code>String</code>,
* AccessDecisionManager} (or delegate), this method should return such 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>AccessDecisionManager</code> (or delegate) to specifically support the <code>GrantedAuthority</code>
* implementation, so returning <code>null</code> should be avoided unless actually required.</p>
* <code>AccessDecisionManager</code> (or delegate) to specifically support the <code>GrantedAuthority</code>
* 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
* expressed as a <code>String</code> with sufficient precision).

View File

@ -15,11 +15,11 @@
package org.springframework.security.core.userdetails;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Comparator;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
@ -40,7 +40,7 @@ public class User implements UserDetails {
//~ Instance fields ================================================================================================
private final String password;
private final String username;
private final List<GrantedAuthority> authorities;
private final Set<GrantedAuthority> authorities;
private final boolean accountNonExpired;
private final boolean accountNonLocked;
private final boolean credentialsNonExpired;
@ -78,7 +78,7 @@ public class User implements UserDetails {
*
* @throws IllegalArgumentException if a <code>null</code> value was passed
* 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,
boolean credentialsNonExpired, boolean accountNonLocked, Collection<GrantedAuthority> authorities) {
@ -93,7 +93,7 @@ public class User implements UserDetails {
this.accountNonExpired = accountNonExpired;
this.credentialsNonExpired = credentialsNonExpired;
this.accountNonLocked = accountNonLocked;
this.authorities = Collections.unmodifiableList(sortAuthorities(authorities));
this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
}
//~ Methods ========================================================================================================
@ -105,7 +105,7 @@ public class User implements UserDetails {
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
if (!authorities.equals(user.authorities)) {
return false;
@ -134,10 +134,8 @@ public class User implements UserDetails {
public int hashCode() {
int code = 9792;
if (this.getAuthorities() != null) {
for (int i = 0; i < this.getAuthorities().size(); i++) {
code = code * (authorities.get(i).hashCode() % 7);
}
for (GrantedAuthority authority : getAuthorities()) {
code = code * (authority.hashCode() % 7);
}
if (this.getPassword() != null) {
@ -183,19 +181,31 @@ public class User implements UserDetails {
return enabled;
}
private static List<GrantedAuthority> sortAuthorities(Collection<GrantedAuthority> authorities) {
Assert.notNull(authorities, "Cannot pass a null GrantedAuthority array");
// Ensure array iteration order is predictable (as per UserDetails.getAuthorities() contract and SEC-xxx)
SortedSet<GrantedAuthority> sorter = new TreeSet<GrantedAuthority>();
private static SortedSet<GrantedAuthority> sortAuthorities(Collection<GrantedAuthority> authorities) {
Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection");
// Ensure array iteration order is predictable (as per UserDetails.getAuthorities() contract and SEC-717)
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) {
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;
}
@ -212,12 +222,14 @@ public class User implements UserDetails {
if (this.getAuthorities() != null) {
sb.append("Granted Authorities: ");
for (int i = 0; i < authorities.size(); i++) {
if (i > 0) {
boolean first = true;
for (GrantedAuthority auth : authorities) {
if (!first) {
sb.append(", ");
first = false;
}
sb.append(authorities.get(i));
sb.append(auth);
}
} else {
sb.append("Not granted any authorities");