SEC-2915: Updated Java Code Formatting

This commit is contained in:
Rob Winch 2015-03-23 11:21:19 -05:00
parent 0a2e496a84
commit ae6af5d73c
1420 changed files with 92995 additions and 85097 deletions

View File

@ -40,201 +40,234 @@ import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* <p>
* Given a domain object instance passed as a method argument, ensures the principal has appropriate permission
* as indicated by the {@link AclService}.
* Given a domain object instance passed as a method argument, ensures the principal has
* appropriate permission as indicated by the {@link AclService}.
* <p>
* The <tt>AclService</tt> is used to retrieve the access control list (ACL) permissions associated with a
* domain object instance for the current <tt>Authentication</tt> object.
* The <tt>AclService</tt> is used to retrieve the access control list (ACL) permissions
* associated with a domain object instance for the current <tt>Authentication</tt>
* object.
* <p>
* The voter will vote if any {@link ConfigAttribute#getAttribute()} matches the {@link #processConfigAttribute}.
* The provider will then locate the first method argument of type {@link #processDomainObjectClass}. Assuming that
* method argument is non-null, the provider will then lookup the ACLs from the <code>AclManager</code> and ensure the
* principal is {@link Acl#isGranted(List,
* List, boolean)} when presenting the {@link #requirePermission} array to that
* method.
* The voter will vote if any {@link ConfigAttribute#getAttribute()} matches the
* {@link #processConfigAttribute}. The provider will then locate the first method
* argument of type {@link #processDomainObjectClass}. Assuming that method argument is
* non-null, the provider will then lookup the ACLs from the <code>AclManager</code> and
* ensure the principal is {@link Acl#isGranted(List, List, boolean)} when presenting the
* {@link #requirePermission} array to that method.
* <p>
* If the method argument is <tt>null</tt>, the voter will abstain from voting. If the method argument
* could not be found, an {@link AuthorizationServiceException} will be thrown.
* If the method argument is <tt>null</tt>, the voter will abstain from voting. If the
* method argument could not be found, an {@link AuthorizationServiceException} will be
* thrown.
* <p>
* In practical terms users will typically setup a number of <tt>AclEntryVoter</tt>s. Each will have a
* different {@link #setProcessDomainObjectClass processDomainObjectClass}, {@link #processConfigAttribute} and
* {@link #requirePermission} combination. For example, a small application might employ the following instances of
* In practical terms users will typically setup a number of <tt>AclEntryVoter</tt>s. Each
* will have a different {@link #setProcessDomainObjectClass processDomainObjectClass},
* {@link #processConfigAttribute} and {@link #requirePermission} combination. For
* example, a small application might employ the following instances of
* <tt>AclEntryVoter</tt>:
* <ul>
* <li>Process domain object class <code>BankAccount</code>, configuration attribute
* <code>VOTE_ACL_BANK_ACCONT_READ</code>, require permission <code>BasePermission.READ</code></li>
* <li>Process domain object class <code>BankAccount</code>, configuration attribute
* <code>VOTE_ACL_BANK_ACCOUNT_WRITE</code>, require permission list <code>BasePermission.WRITE</code> and
* <code>BasePermission.CREATE</code> (allowing the principal to have <b>either</b> of these two permissions)</li>
* <li>Process domain object class <code>Customer</code>, configuration attribute
* <code>VOTE_ACL_CUSTOMER_READ</code>, require permission <code>BasePermission.READ</code></li>
* <li>Process domain object class <code>Customer</code>, configuration attribute
* <code>VOTE_ACL_CUSTOMER_WRITE</code>, require permission list <code>BasePermission.WRITE</code> and
* <code>BasePermission.CREATE</code></li>
* </ul>
* Alternatively, you could have used a common superclass or interface for the {@link #processDomainObjectClass}
* if both <code>BankAccount</code> and <code>Customer</code> had common parents.</p>
* <p>If the principal does not have sufficient permissions, the voter will vote to deny access.</p>
* <p>All comparisons and prefixes are case sensitive.</p>
* <ul>
* <li>Process domain object class <code>BankAccount</code>, configuration attribute
* <code>VOTE_ACL_BANK_ACCONT_READ</code>, require permission
* <code>BasePermission.READ</code></li>
* <li>Process domain object class <code>BankAccount</code>, configuration attribute
* <code>VOTE_ACL_BANK_ACCOUNT_WRITE</code>, require permission list
* <code>BasePermission.WRITE</code> and <code>BasePermission.CREATE</code> (allowing the
* principal to have <b>either</b> of these two permissions)</li>
* <li>Process domain object class <code>Customer</code>, configuration attribute
* <code>VOTE_ACL_CUSTOMER_READ</code>, require permission
* <code>BasePermission.READ</code></li>
* <li>Process domain object class <code>Customer</code>, configuration attribute
* <code>VOTE_ACL_CUSTOMER_WRITE</code>, require permission list
* <code>BasePermission.WRITE</code> and <code>BasePermission.CREATE</code></li>
* </ul>
* Alternatively, you could have used a common superclass or interface for the
* {@link #processDomainObjectClass} if both <code>BankAccount</code> and
* <code>Customer</code> had common parents.
* </p>
* <p>
* If the principal does not have sufficient permissions, the voter will vote to deny
* access.
* </p>
* <p>
* All comparisons and prefixes are case sensitive.
* </p>
*
* @author Ben Alex
*/
public class AclEntryVoter extends AbstractAclVoter {
//~ Static fields/initializers =====================================================================================
// ~ Static fields/initializers
// =====================================================================================
private static final Log logger = LogFactory.getLog(AclEntryVoter.class);
private static final Log logger = LogFactory.getLog(AclEntryVoter.class);
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private AclService aclService;
private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
private String internalMethod;
private String processConfigAttribute;
private List<Permission> requirePermission;
private AclService aclService;
private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
private String internalMethod;
private String processConfigAttribute;
private List<Permission> requirePermission;
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
public AclEntryVoter(AclService aclService, String processConfigAttribute, Permission[] requirePermission) {
Assert.notNull(processConfigAttribute, "A processConfigAttribute is mandatory");
Assert.notNull(aclService, "An AclService is mandatory");
public AclEntryVoter(AclService aclService, String processConfigAttribute,
Permission[] requirePermission) {
Assert.notNull(processConfigAttribute, "A processConfigAttribute is mandatory");
Assert.notNull(aclService, "An AclService is mandatory");
if ((requirePermission == null) || (requirePermission.length == 0)) {
throw new IllegalArgumentException("One or more requirePermission entries is mandatory");
}
if ((requirePermission == null) || (requirePermission.length == 0)) {
throw new IllegalArgumentException(
"One or more requirePermission entries is mandatory");
}
this.aclService = aclService;
this.processConfigAttribute = processConfigAttribute;
this.requirePermission = Arrays.asList(requirePermission);
}
this.aclService = aclService;
this.processConfigAttribute = processConfigAttribute;
this.requirePermission = Arrays.asList(requirePermission);
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
/**
* Optionally specifies a method of the domain object that will be used to obtain a contained domain
* object. That contained domain object will be used for the ACL evaluation. This is useful if a domain object
* contains a parent that an ACL evaluation should be targeted for, instead of the child domain object (which
* perhaps is being created and as such does not yet have any ACL permissions)
*
* @return <code>null</code> to use the domain object, or the name of a method (that requires no arguments) that
* should be invoked to obtain an <code>Object</code> which will be the domain object used for ACL
* evaluation
*/
protected String getInternalMethod() {
return internalMethod;
}
/**
* Optionally specifies a method of the domain object that will be used to obtain a
* contained domain object. That contained domain object will be used for the ACL
* evaluation. This is useful if a domain object contains a parent that an ACL
* evaluation should be targeted for, instead of the child domain object (which
* perhaps is being created and as such does not yet have any ACL permissions)
*
* @return <code>null</code> to use the domain object, or the name of a method (that
* requires no arguments) that should be invoked to obtain an <code>Object</code>
* which will be the domain object used for ACL evaluation
*/
protected String getInternalMethod() {
return internalMethod;
}
public void setInternalMethod(String internalMethod) {
this.internalMethod = internalMethod;
}
public void setInternalMethod(String internalMethod) {
this.internalMethod = internalMethod;
}
protected String getProcessConfigAttribute() {
return processConfigAttribute;
}
protected String getProcessConfigAttribute() {
return processConfigAttribute;
}
public void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
Assert.notNull(objectIdentityRetrievalStrategy, "ObjectIdentityRetrievalStrategy required");
this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
}
public void setObjectIdentityRetrievalStrategy(
ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
Assert.notNull(objectIdentityRetrievalStrategy,
"ObjectIdentityRetrievalStrategy required");
this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required");
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required");
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
public boolean supports(ConfigAttribute attribute) {
return (attribute.getAttribute() != null) && attribute.getAttribute().equals(getProcessConfigAttribute());
}
public boolean supports(ConfigAttribute attribute) {
return (attribute.getAttribute() != null)
&& attribute.getAttribute().equals(getProcessConfigAttribute());
}
public int vote(Authentication authentication, MethodInvocation object, Collection<ConfigAttribute> attributes) {
public int vote(Authentication authentication, MethodInvocation object,
Collection<ConfigAttribute> attributes) {
for(ConfigAttribute attr : attributes) {
for (ConfigAttribute attr : attributes) {
if (!this.supports(attr)) {
continue;
}
// Need to make an access decision on this invocation
// Attempt to locate the domain object instance to process
Object domainObject = getDomainObjectInstance(object);
if (!this.supports(attr)) {
continue;
}
// Need to make an access decision on this invocation
// Attempt to locate the domain object instance to process
Object domainObject = getDomainObjectInstance(object);
// If domain object is null, vote to abstain
if (domainObject == null) {
if (logger.isDebugEnabled()) {
logger.debug("Voting to abstain - domainObject is null");
}
// If domain object is null, vote to abstain
if (domainObject == null) {
if (logger.isDebugEnabled()) {
logger.debug("Voting to abstain - domainObject is null");
}
return ACCESS_ABSTAIN;
}
return ACCESS_ABSTAIN;
}
// Evaluate if we are required to use an inner domain object
if (StringUtils.hasText(internalMethod)) {
try {
Class<?> clazz = domainObject.getClass();
Method method = clazz.getMethod(internalMethod, new Class[0]);
domainObject = method.invoke(domainObject);
} catch (NoSuchMethodException nsme) {
throw new AuthorizationServiceException("Object of class '" + domainObject.getClass()
+ "' does not provide the requested internalMethod: " + internalMethod);
} catch (IllegalAccessException iae) {
logger.debug("IllegalAccessException", iae);
// Evaluate if we are required to use an inner domain object
if (StringUtils.hasText(internalMethod)) {
try {
Class<?> clazz = domainObject.getClass();
Method method = clazz.getMethod(internalMethod, new Class[0]);
domainObject = method.invoke(domainObject);
}
catch (NoSuchMethodException nsme) {
throw new AuthorizationServiceException("Object of class '"
+ domainObject.getClass()
+ "' does not provide the requested internalMethod: "
+ internalMethod);
}
catch (IllegalAccessException iae) {
logger.debug("IllegalAccessException", iae);
throw new AuthorizationServiceException("Problem invoking internalMethod: " + internalMethod
+ " for object: " + domainObject);
} catch (InvocationTargetException ite) {
logger.debug("InvocationTargetException", ite);
throw new AuthorizationServiceException(
"Problem invoking internalMethod: " + internalMethod
+ " for object: " + domainObject);
}
catch (InvocationTargetException ite) {
logger.debug("InvocationTargetException", ite);
throw new AuthorizationServiceException("Problem invoking internalMethod: " + internalMethod
+ " for object: " + domainObject);
}
}
throw new AuthorizationServiceException(
"Problem invoking internalMethod: " + internalMethod
+ " for object: " + domainObject);
}
}
// Obtain the OID applicable to the domain object
ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy.getObjectIdentity(domainObject);
// Obtain the OID applicable to the domain object
ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy
.getObjectIdentity(domainObject);
// Obtain the SIDs applicable to the principal
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
// Obtain the SIDs applicable to the principal
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
Acl acl;
Acl acl;
try {
// Lookup only ACLs for SIDs we're interested in
acl = aclService.readAclById(objectIdentity, sids);
} catch (NotFoundException nfe) {
if (logger.isDebugEnabled()) {
logger.debug("Voting to deny access - no ACLs apply for this principal");
}
try {
// Lookup only ACLs for SIDs we're interested in
acl = aclService.readAclById(objectIdentity, sids);
}
catch (NotFoundException nfe) {
if (logger.isDebugEnabled()) {
logger.debug("Voting to deny access - no ACLs apply for this principal");
}
return ACCESS_DENIED;
}
return ACCESS_DENIED;
}
try {
if (acl.isGranted(requirePermission, sids, false)) {
if (logger.isDebugEnabled()) {
logger.debug("Voting to grant access");
}
try {
if (acl.isGranted(requirePermission, sids, false)) {
if (logger.isDebugEnabled()) {
logger.debug("Voting to grant access");
}
return ACCESS_GRANTED;
} else {
if (logger.isDebugEnabled()) {
logger.debug(
"Voting to deny access - ACLs returned, but insufficient permissions for this principal");
}
return ACCESS_GRANTED;
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Voting to deny access - ACLs returned, but insufficient permissions for this principal");
}
return ACCESS_DENIED;
}
} catch (NotFoundException nfe) {
if (logger.isDebugEnabled()) {
logger.debug("Voting to deny access - no ACLs apply for this principal");
}
return ACCESS_DENIED;
}
}
catch (NotFoundException nfe) {
if (logger.isDebugEnabled()) {
logger.debug("Voting to deny access - no ACLs apply for this principal");
}
return ACCESS_DENIED;
}
}
return ACCESS_DENIED;
}
}
// No configuration attribute matched, so abstain
return ACCESS_ABSTAIN;
}
// No configuration attribute matched, so abstain
return ACCESS_ABSTAIN;
}
}

View File

@ -23,44 +23,45 @@ import org.springframework.security.core.Authentication;
* @since 3.1
*/
public class AclPermissionCacheOptimizer implements PermissionCacheOptimizer {
private final Log logger = LogFactory.getLog(getClass());
private final AclService aclService;
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
private ObjectIdentityRetrievalStrategy oidRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
private final Log logger = LogFactory.getLog(getClass());
private final AclService aclService;
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
private ObjectIdentityRetrievalStrategy oidRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
public AclPermissionCacheOptimizer(AclService aclService) {
this.aclService = aclService;
}
public AclPermissionCacheOptimizer(AclService aclService) {
this.aclService = aclService;
}
public void cachePermissionsFor(Authentication authentication, Collection<?> objects) {
if (objects.isEmpty()) {
return;
}
public void cachePermissionsFor(Authentication authentication, Collection<?> objects) {
if (objects.isEmpty()) {
return;
}
List<ObjectIdentity> oidsToCache = new ArrayList<ObjectIdentity>(objects.size());
List<ObjectIdentity> oidsToCache = new ArrayList<ObjectIdentity>(objects.size());
for (Object domainObject : objects) {
if (domainObject == null) {
continue;
}
ObjectIdentity oid = oidRetrievalStrategy.getObjectIdentity(domainObject);
oidsToCache.add(oid);
}
for (Object domainObject : objects) {
if (domainObject == null) {
continue;
}
ObjectIdentity oid = oidRetrievalStrategy.getObjectIdentity(domainObject);
oidsToCache.add(oid);
}
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
if (logger.isDebugEnabled()) {
logger.debug("Eagerly loading Acls for " + oidsToCache.size() + " objects");
}
if (logger.isDebugEnabled()) {
logger.debug("Eagerly loading Acls for " + oidsToCache.size() + " objects");
}
aclService.readAclsById(oidsToCache, sids);
}
aclService.readAclsById(oidsToCache, sids);
}
public void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
this.oidRetrievalStrategy = objectIdentityRetrievalStrategy;
}
public void setObjectIdentityRetrievalStrategy(
ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
this.oidRetrievalStrategy = objectIdentityRetrievalStrategy;
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
}

View File

@ -23,8 +23,8 @@ import org.springframework.security.acls.model.SidRetrievalStrategy;
import org.springframework.security.core.Authentication;
/**
* Used by Spring Security's expression-based access control implementation to evaluate permissions for a particular
* object using the ACL module. Similar in behaviour to
* Used by Spring Security's expression-based access control implementation to evaluate
* permissions for a particular object using the ACL module. Similar in behaviour to
* {@link org.springframework.security.acls.AclEntryVoter AclEntryVoter}.
*
* @author Luke Taylor
@ -32,120 +32,130 @@ import org.springframework.security.core.Authentication;
*/
public class AclPermissionEvaluator implements PermissionEvaluator {
private final Log logger = LogFactory.getLog(getClass());
private final Log logger = LogFactory.getLog(getClass());
private final AclService aclService;
private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
private ObjectIdentityGenerator objectIdentityGenerator = new ObjectIdentityRetrievalStrategyImpl();
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
private PermissionFactory permissionFactory = new DefaultPermissionFactory();
private final AclService aclService;
private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
private ObjectIdentityGenerator objectIdentityGenerator = new ObjectIdentityRetrievalStrategyImpl();
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
private PermissionFactory permissionFactory = new DefaultPermissionFactory();
public AclPermissionEvaluator(AclService aclService) {
this.aclService = aclService;
}
public AclPermissionEvaluator(AclService aclService) {
this.aclService = aclService;
}
/**
* Determines whether the user has the given permission(s) on the domain object using the ACL
* configuration. If the domain object is null, returns false (this can always be overridden using a null
* check in the expression itself).
*/
public boolean hasPermission(Authentication authentication, Object domainObject, Object permission) {
if (domainObject == null) {
return false;
}
/**
* Determines whether the user has the given permission(s) on the domain object using
* the ACL configuration. If the domain object is null, returns false (this can always
* be overridden using a null check in the expression itself).
*/
public boolean hasPermission(Authentication authentication, Object domainObject,
Object permission) {
if (domainObject == null) {
return false;
}
ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy.getObjectIdentity(domainObject);
ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy
.getObjectIdentity(domainObject);
return checkPermission(authentication, objectIdentity, permission);
}
return checkPermission(authentication, objectIdentity, permission);
}
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
ObjectIdentity objectIdentity = objectIdentityGenerator.createObjectIdentity(targetId, targetType);
public boolean hasPermission(Authentication authentication, Serializable targetId,
String targetType, Object permission) {
ObjectIdentity objectIdentity = objectIdentityGenerator.createObjectIdentity(
targetId, targetType);
return checkPermission(authentication, objectIdentity, permission);
}
return checkPermission(authentication, objectIdentity, permission);
}
private boolean checkPermission(Authentication authentication, ObjectIdentity oid, Object permission) {
// Obtain the SIDs applicable to the principal
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
List<Permission> requiredPermission = resolvePermission(permission);
private boolean checkPermission(Authentication authentication, ObjectIdentity oid,
Object permission) {
// Obtain the SIDs applicable to the principal
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
List<Permission> requiredPermission = resolvePermission(permission);
final boolean debug = logger.isDebugEnabled();
final boolean debug = logger.isDebugEnabled();
if (debug) {
logger.debug("Checking permission '" + permission + "' for object '" + oid + "'");
}
if (debug) {
logger.debug("Checking permission '" + permission + "' for object '" + oid
+ "'");
}
try {
// Lookup only ACLs for SIDs we're interested in
Acl acl = aclService.readAclById(oid, sids);
try {
// Lookup only ACLs for SIDs we're interested in
Acl acl = aclService.readAclById(oid, sids);
if (acl.isGranted(requiredPermission, sids, false)) {
if (debug) {
logger.debug("Access is granted");
}
if (acl.isGranted(requiredPermission, sids, false)) {
if (debug) {
logger.debug("Access is granted");
}
return true;
}
return true;
}
if (debug) {
logger.debug("Returning false - ACLs returned, but insufficient permissions for this principal");
}
if (debug) {
logger.debug("Returning false - ACLs returned, but insufficient permissions for this principal");
}
} catch (NotFoundException nfe) {
if (debug) {
logger.debug("Returning false - no ACLs apply for this principal");
}
}
}
catch (NotFoundException nfe) {
if (debug) {
logger.debug("Returning false - no ACLs apply for this principal");
}
}
return false;
return false;
}
}
List<Permission> resolvePermission(Object permission) {
if (permission instanceof Integer) {
return Arrays.asList(permissionFactory.buildFromMask(((Integer)permission).intValue()));
}
List<Permission> resolvePermission(Object permission) {
if (permission instanceof Integer) {
return Arrays.asList(permissionFactory.buildFromMask(((Integer) permission)
.intValue()));
}
if (permission instanceof Permission) {
return Arrays.asList((Permission)permission);
}
if (permission instanceof Permission) {
return Arrays.asList((Permission) permission);
}
if (permission instanceof Permission[]) {
return Arrays.asList((Permission[])permission);
}
if (permission instanceof Permission[]) {
return Arrays.asList((Permission[]) permission);
}
if (permission instanceof String) {
String permString = (String)permission;
Permission p;
if (permission instanceof String) {
String permString = (String) permission;
Permission p;
try {
p = permissionFactory.buildFromName(permString);
} catch(IllegalArgumentException notfound) {
p = permissionFactory.buildFromName(permString.toUpperCase());
}
try {
p = permissionFactory.buildFromName(permString);
}
catch (IllegalArgumentException notfound) {
p = permissionFactory.buildFromName(permString.toUpperCase());
}
if (p != null) {
return Arrays.asList(p);
}
if (p != null) {
return Arrays.asList(p);
}
}
throw new IllegalArgumentException("Unsupported permission: " + permission);
}
}
throw new IllegalArgumentException("Unsupported permission: " + permission);
}
public void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
}
public void setObjectIdentityRetrievalStrategy(
ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
}
public void setObjectIdentityGenerator(ObjectIdentityGenerator objectIdentityGenerator) {
this.objectIdentityGenerator = objectIdentityGenerator;
}
public void setObjectIdentityGenerator(ObjectIdentityGenerator objectIdentityGenerator) {
this.objectIdentityGenerator = objectIdentityGenerator;
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
public void setPermissionFactory(PermissionFactory permissionFactory) {
this.permissionFactory = permissionFactory;
}
public void setPermissionFactory(PermissionFactory permissionFactory) {
this.permissionFactory = permissionFactory;
}
}

View File

@ -34,92 +34,103 @@ import org.springframework.security.acls.model.SidRetrievalStrategy;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* Abstract {@link AfterInvocationProvider} which provides commonly-used ACL-related services.
* Abstract {@link AfterInvocationProvider} which provides commonly-used ACL-related
* services.
*
* @author Ben Alex
*/
*/
public abstract class AbstractAclProvider implements AfterInvocationProvider {
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
protected final AclService aclService;
protected Class<?> processDomainObjectClass = Object.class;
protected ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
protected SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
protected String processConfigAttribute;
protected final List<Permission> requirePermission;
protected final AclService aclService;
protected Class<?> processDomainObjectClass = Object.class;
protected ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
protected SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
protected String processConfigAttribute;
protected final List<Permission> requirePermission;
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
public AbstractAclProvider(AclService aclService, String processConfigAttribute, List<Permission> requirePermission) {
Assert.hasText(processConfigAttribute, "A processConfigAttribute is mandatory");
Assert.notNull(aclService, "An AclService is mandatory");
public AbstractAclProvider(AclService aclService, String processConfigAttribute,
List<Permission> requirePermission) {
Assert.hasText(processConfigAttribute, "A processConfigAttribute is mandatory");
Assert.notNull(aclService, "An AclService is mandatory");
if (requirePermission == null || requirePermission.isEmpty()) {
throw new IllegalArgumentException("One or more requirePermission entries is mandatory");
}
if (requirePermission == null || requirePermission.isEmpty()) {
throw new IllegalArgumentException(
"One or more requirePermission entries is mandatory");
}
this.aclService = aclService;
this.processConfigAttribute = processConfigAttribute;
this.requirePermission = requirePermission;
}
this.aclService = aclService;
this.processConfigAttribute = processConfigAttribute;
this.requirePermission = requirePermission;
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
protected Class<?> getProcessDomainObjectClass() {
return processDomainObjectClass;
}
protected Class<?> getProcessDomainObjectClass() {
return processDomainObjectClass;
}
protected boolean hasPermission(Authentication authentication, Object domainObject) {
// Obtain the OID applicable to the domain object
ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy.getObjectIdentity(domainObject);
protected boolean hasPermission(Authentication authentication, Object domainObject) {
// Obtain the OID applicable to the domain object
ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy
.getObjectIdentity(domainObject);
// Obtain the SIDs applicable to the principal
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
// Obtain the SIDs applicable to the principal
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
try {
// Lookup only ACLs for SIDs we're interested in
Acl acl = aclService.readAclById(objectIdentity, sids);
try {
// Lookup only ACLs for SIDs we're interested in
Acl acl = aclService.readAclById(objectIdentity, sids);
return acl.isGranted(requirePermission, sids, false);
} catch (NotFoundException ignore) {
return false;
}
}
return acl.isGranted(requirePermission, sids, false);
}
catch (NotFoundException ignore) {
return false;
}
}
public void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
Assert.notNull(objectIdentityRetrievalStrategy, "ObjectIdentityRetrievalStrategy required");
this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
}
public void setObjectIdentityRetrievalStrategy(
ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
Assert.notNull(objectIdentityRetrievalStrategy,
"ObjectIdentityRetrievalStrategy required");
this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
}
protected void setProcessConfigAttribute(String processConfigAttribute) {
Assert.hasText(processConfigAttribute, "A processConfigAttribute is mandatory");
this.processConfigAttribute = processConfigAttribute;
}
protected void setProcessConfigAttribute(String processConfigAttribute) {
Assert.hasText(processConfigAttribute, "A processConfigAttribute is mandatory");
this.processConfigAttribute = processConfigAttribute;
}
public void setProcessDomainObjectClass(Class<?> processDomainObjectClass) {
Assert.notNull(processDomainObjectClass, "processDomainObjectClass cannot be set to null");
this.processDomainObjectClass = processDomainObjectClass;
}
public void setProcessDomainObjectClass(Class<?> processDomainObjectClass) {
Assert.notNull(processDomainObjectClass,
"processDomainObjectClass cannot be set to null");
this.processDomainObjectClass = processDomainObjectClass;
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required");
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required");
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
public boolean supports(ConfigAttribute attribute) {
return processConfigAttribute.equals(attribute.getAttribute());
}
public boolean supports(ConfigAttribute attribute) {
return processConfigAttribute.equals(attribute.getAttribute());
}
/**
* This implementation supports any type of class, because it does not query the presented secure object.
*
* @param clazz the secure object
*
* @return always <code>true</code>
*/
public boolean supports(Class<?> clazz) {
return true;
}
/**
* This implementation supports any type of class, because it does not query the
* presented secure object.
*
* @param clazz the secure object
*
* @return always <code>true</code>
*/
public boolean supports(Class<?> clazz) {
return true;
}
}

View File

@ -26,96 +26,113 @@ import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.core.Authentication;
/**
* <p>
* Given a <code>Collection</code> of domain object instances returned from a secure object invocation, remove
* any <code>Collection</code> elements the principal does not have appropriate permission to access as defined by the
* {@link AclService}.
* Given a <code>Collection</code> of domain object instances returned from a secure
* object invocation, remove any <code>Collection</code> elements the principal does not
* have appropriate permission to access as defined by the {@link AclService}.
* <p>
* The <code>AclService</code> is used to retrieve the access control list (ACL) permissions associated with
* each <code>Collection</code> domain object instance element for the current <code>Authentication</code> object.
* The <code>AclService</code> is used to retrieve the access control list (ACL)
* permissions associated with each <code>Collection</code> domain object instance element
* for the current <code>Authentication</code> object.
* <p>
* This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()} matches the {@link
* #processConfigAttribute}. The provider will then lookup the ACLs from the <code>AclService</code> and ensure the
* principal is {@link org.springframework.security.acls.model.Acl#isGranted(List, List, boolean) Acl.isGranted()}
* when presenting the {@link #requirePermission} array to that method.
* This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()}
* matches the {@link #processConfigAttribute}. The provider will then lookup the ACLs
* from the <code>AclService</code> and ensure the principal is
* {@link org.springframework.security.acls.model.Acl#isGranted(List, List, boolean)
* Acl.isGranted()} when presenting the {@link #requirePermission} array to that method.
* <p>
* If the principal does not have permission, that element will not be included in the returned
* <code>Collection</code>.
* If the principal does not have permission, that element will not be included in the
* returned <code>Collection</code>.
* <p>
* Often users will setup a <code>BasicAclEntryAfterInvocationProvider</code> with a {@link
* #processConfigAttribute} of <code>AFTER_ACL_COLLECTION_READ</code> and a {@link #requirePermission} of
* <code>BasePermission.READ</code>. These are also the defaults.
* Often users will setup a <code>BasicAclEntryAfterInvocationProvider</code> with a
* {@link #processConfigAttribute} of <code>AFTER_ACL_COLLECTION_READ</code> and a
* {@link #requirePermission} of <code>BasePermission.READ</code>. These are also the
* defaults.
* <p>
* If the provided <code>returnObject</code> is <code>null</code>, a <code>null</code><code>Collection</code>
* will be returned. If the provided <code>returnObject</code> is not a <code>Collection</code>, an {@link
* AuthorizationServiceException} will be thrown.
* If the provided <code>returnObject</code> is <code>null</code>, a <code>null</code>
* <code>Collection</code> will be returned. If the provided <code>returnObject</code> is
* not a <code>Collection</code>, an {@link AuthorizationServiceException} will be thrown.
* <p>
* All comparisons and prefixes are case sensitive.
*
* @author Ben Alex
* @author Paulo Neves
*/
public class AclEntryAfterInvocationCollectionFilteringProvider extends AbstractAclProvider {
//~ Static fields/initializers =====================================================================================
public class AclEntryAfterInvocationCollectionFilteringProvider extends
AbstractAclProvider {
// ~ Static fields/initializers
// =====================================================================================
protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationCollectionFilteringProvider.class);
protected static final Log logger = LogFactory
.getLog(AclEntryAfterInvocationCollectionFilteringProvider.class);
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
public AclEntryAfterInvocationCollectionFilteringProvider(AclService aclService, List<Permission> requirePermission) {
super(aclService, "AFTER_ACL_COLLECTION_READ", requirePermission);
}
public AclEntryAfterInvocationCollectionFilteringProvider(AclService aclService,
List<Permission> requirePermission) {
super(aclService, "AFTER_ACL_COLLECTION_READ", requirePermission);
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
@SuppressWarnings("unchecked")
public Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> config,
Object returnedObject) throws AccessDeniedException {
@SuppressWarnings("unchecked")
public Object decide(Authentication authentication, Object object,
Collection<ConfigAttribute> config, Object returnedObject)
throws AccessDeniedException {
if (returnedObject == null) {
logger.debug("Return object is null, skipping");
if (returnedObject == null) {
logger.debug("Return object is null, skipping");
return null;
}
return null;
}
for (ConfigAttribute attr : config) {
if (!this.supports(attr)) {
continue;
}
for (ConfigAttribute attr : config) {
if (!this.supports(attr)) {
continue;
}
// Need to process the Collection for this invocation
Filterer filterer;
// Need to process the Collection for this invocation
Filterer filterer;
if (returnedObject instanceof Collection) {
filterer = new CollectionFilterer((Collection) returnedObject);
} else if (returnedObject.getClass().isArray()) {
filterer = new ArrayFilterer((Object[]) returnedObject);
} else {
throw new AuthorizationServiceException("A Collection or an array (or null) was required as the "
+ "returnedObject, but the returnedObject was: " + returnedObject);
}
if (returnedObject instanceof Collection) {
filterer = new CollectionFilterer((Collection) returnedObject);
}
else if (returnedObject.getClass().isArray()) {
filterer = new ArrayFilterer((Object[]) returnedObject);
}
else {
throw new AuthorizationServiceException(
"A Collection or an array (or null) was required as the "
+ "returnedObject, but the returnedObject was: "
+ returnedObject);
}
// Locate unauthorised Collection elements
for (Object domainObject : filterer) {
// Ignore nulls or entries which aren't instances of the configured domain object class
if (domainObject == null || !getProcessDomainObjectClass().isAssignableFrom(domainObject.getClass())) {
continue;
}
// Locate unauthorised Collection elements
for (Object domainObject : filterer) {
// Ignore nulls or entries which aren't instances of the configured domain
// object class
if (domainObject == null
|| !getProcessDomainObjectClass().isAssignableFrom(
domainObject.getClass())) {
continue;
}
if(!hasPermission(authentication, domainObject)) {
filterer.remove(domainObject);
if (!hasPermission(authentication, domainObject)) {
filterer.remove(domainObject);
if (logger.isDebugEnabled()) {
logger.debug("Principal is NOT authorised for element: " + domainObject);
}
}
}
if (logger.isDebugEnabled()) {
logger.debug("Principal is NOT authorised for element: "
+ domainObject);
}
}
}
return filterer.getFilteredObject();
}
return filterer.getFilteredObject();
}
return returnedObject;
}
return returnedObject;
}
}

View File

@ -29,90 +29,103 @@ import org.springframework.security.acls.model.Permission;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.SpringSecurityMessageSource;
/**
* Given a domain object instance returned from a secure object invocation, ensures the principal has
* appropriate permission as defined by the {@link AclService}.
* Given a domain object instance returned from a secure object invocation, ensures the
* principal has appropriate permission as defined by the {@link AclService}.
* <p>
* The <code>AclService</code> is used to retrieve the access control list (ACL) permissions associated with a
* domain object instance for the current <code>Authentication</code> object.
* The <code>AclService</code> is used to retrieve the access control list (ACL)
* permissions associated with a domain object instance for the current
* <code>Authentication</code> object.
* <p>
* This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()} matches the {@link
* #processConfigAttribute}. The provider will then lookup the ACLs from the <tt>AclService</tt> and ensure the
* principal is {@link org.springframework.security.acls.model.Acl#isGranted(List, List, boolean)
* Acl.isGranted(List, List, boolean)} when presenting the {@link #requirePermission} array to that method.
* This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()}
* matches the {@link #processConfigAttribute}. The provider will then lookup the ACLs
* from the <tt>AclService</tt> and ensure the principal is
* {@link org.springframework.security.acls.model.Acl#isGranted(List, List, boolean)
* Acl.isGranted(List, List, boolean)} when presenting the {@link #requirePermission}
* array to that method.
* <p>
* Often users will set up an <code>AclEntryAfterInvocationProvider</code> with a {@link
* #processConfigAttribute} of <code>AFTER_ACL_READ</code> and a {@link #requirePermission} of
* <code>BasePermission.READ</code>. These are also the defaults.
* Often users will set up an <code>AclEntryAfterInvocationProvider</code> with a
* {@link #processConfigAttribute} of <code>AFTER_ACL_READ</code> and a
* {@link #requirePermission} of <code>BasePermission.READ</code>. These are also the
* defaults.
* <p>
* If the principal does not have sufficient permissions, an <code>AccessDeniedException</code> will be thrown.
* If the principal does not have sufficient permissions, an
* <code>AccessDeniedException</code> will be thrown.
* <p>
* If the provided <tt>returnedObject</tt> is <code>null</code>, permission will always be granted and
* <code>null</code> will be returned.
* If the provided <tt>returnedObject</tt> is <code>null</code>, permission will always be
* granted and <code>null</code> will be returned.
* <p>
* All comparisons and prefixes are case sensitive.
*/
public class AclEntryAfterInvocationProvider extends AbstractAclProvider implements MessageSourceAware {
//~ Static fields/initializers =====================================================================================
public class AclEntryAfterInvocationProvider extends AbstractAclProvider implements
MessageSourceAware {
// ~ Static fields/initializers
// =====================================================================================
protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationProvider.class);
protected static final Log logger = LogFactory
.getLog(AclEntryAfterInvocationProvider.class);
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
public AclEntryAfterInvocationProvider(AclService aclService, List<Permission> requirePermission) {
this(aclService, "AFTER_ACL_READ", requirePermission);
}
public AclEntryAfterInvocationProvider(AclService aclService,
List<Permission> requirePermission) {
this(aclService, "AFTER_ACL_READ", requirePermission);
}
public AclEntryAfterInvocationProvider(AclService aclService, String processConfigAttribute,
List<Permission> requirePermission) {
super(aclService, processConfigAttribute, requirePermission);
}
public AclEntryAfterInvocationProvider(AclService aclService,
String processConfigAttribute, List<Permission> requirePermission) {
super(aclService, processConfigAttribute, requirePermission);
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> config,
Object returnedObject) throws AccessDeniedException {
public Object decide(Authentication authentication, Object object,
Collection<ConfigAttribute> config, Object returnedObject)
throws AccessDeniedException {
if (returnedObject == null) {
// AclManager interface contract prohibits nulls
// As they have permission to null/nothing, grant access
logger.debug("Return object is null, skipping");
if (returnedObject == null) {
// AclManager interface contract prohibits nulls
// As they have permission to null/nothing, grant access
logger.debug("Return object is null, skipping");
return null;
}
return null;
}
if (!getProcessDomainObjectClass().isAssignableFrom(returnedObject.getClass())) {
logger.debug("Return object is not applicable for this provider, skipping");
if (!getProcessDomainObjectClass().isAssignableFrom(returnedObject.getClass())) {
logger.debug("Return object is not applicable for this provider, skipping");
return returnedObject;
}
return returnedObject;
}
for (ConfigAttribute attr : config) {
if (!this.supports(attr)) {
continue;
}
// Need to make an access decision on this invocation
for (ConfigAttribute attr : config) {
if (!this.supports(attr)) {
continue;
}
// Need to make an access decision on this invocation
if (hasPermission(authentication, returnedObject)) {
return returnedObject;
}
if (hasPermission(authentication, returnedObject)) {
return returnedObject;
}
logger.debug("Denying access");
logger.debug("Denying access");
throw new AccessDeniedException(messages.getMessage("AclEntryAfterInvocationProvider.noPermission",
new Object[] {authentication.getName(), returnedObject},
"Authentication {0} has NO permissions to the domain object {1}"));
}
throw new AccessDeniedException(messages.getMessage(
"AclEntryAfterInvocationProvider.noPermission", new Object[] {
authentication.getName(), returnedObject },
"Authentication {0} has NO permissions to the domain object {1}"));
}
return returnedObject;
}
return returnedObject;
}
public void setMessageSource(MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
public void setMessageSource(MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
}

View File

@ -24,7 +24,6 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* A filter used to filter arrays.
*
@ -32,86 +31,91 @@ import org.apache.commons.logging.LogFactory;
* @author Paulo Neves
*/
class ArrayFilterer<T> implements Filterer<T> {
//~ Static fields/initializers =====================================================================================
// ~ Static fields/initializers
// =====================================================================================
protected static final Log logger = LogFactory.getLog(ArrayFilterer.class);
protected static final Log logger = LogFactory.getLog(ArrayFilterer.class);
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private final Set<T> removeList;
private final T[] list;
private final Set<T> removeList;
private final T[] list;
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
ArrayFilterer(T[] list) {
this.list = list;
ArrayFilterer(T[] list) {
this.list = list;
// Collect the removed objects to a HashSet so that
// it is fast to lookup them when a filtered array
// is constructed.
removeList = new HashSet<T>();
}
// Collect the removed objects to a HashSet so that
// it is fast to lookup them when a filtered array
// is constructed.
removeList = new HashSet<T>();
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
/**
*
* @see org.springframework.security.acls.afterinvocation.Filterer#getFilteredObject()
*/
@SuppressWarnings("unchecked")
public T[] getFilteredObject() {
// Recreate an array of same type and filter the removed objects.
int originalSize = list.length;
int sizeOfResultingList = originalSize - removeList.size();
T[] filtered = (T[]) Array.newInstance(list.getClass().getComponentType(), sizeOfResultingList);
/**
*
* @see org.springframework.security.acls.afterinvocation.Filterer#getFilteredObject()
*/
@SuppressWarnings("unchecked")
public T[] getFilteredObject() {
// Recreate an array of same type and filter the removed objects.
int originalSize = list.length;
int sizeOfResultingList = originalSize - removeList.size();
T[] filtered = (T[]) Array.newInstance(list.getClass().getComponentType(),
sizeOfResultingList);
for (int i = 0, j = 0; i < list.length; i++) {
T object = list[i];
for (int i = 0, j = 0; i < list.length; i++) {
T object = list[i];
if (!removeList.contains(object)) {
filtered[j] = object;
j++;
}
}
if (!removeList.contains(object)) {
filtered[j] = object;
j++;
}
}
if (logger.isDebugEnabled()) {
logger.debug("Original array contained " + originalSize + " elements; now contains " + sizeOfResultingList
+ " elements");
}
if (logger.isDebugEnabled()) {
logger.debug("Original array contained " + originalSize
+ " elements; now contains " + sizeOfResultingList + " elements");
}
return filtered;
}
return filtered;
}
/**
*
* @see org.springframework.security.acls.afterinvocation.Filterer#iterator()
*/
public Iterator<T> iterator() {
return new Iterator<T>() {
private int index = 0;
/**
*
* @see org.springframework.security.acls.afterinvocation.Filterer#iterator()
*/
public Iterator<T> iterator() {
return new Iterator<T>() {
private int index = 0;
public boolean hasNext() {
return index < list.length;
}
public boolean hasNext() {
return index < list.length;
}
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return list[index++];
}
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return list[index++];
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
*
* @see org.springframework.security.acls.afterinvocation.Filterer#remove(java.lang.Object)
*/
public void remove(T object) {
removeList.add(object);
}
/**
*
* @see org.springframework.security.acls.afterinvocation.Filterer#remove(java.lang.Object)
*/
public void remove(T object) {
removeList.add(object);
}
}

View File

@ -23,7 +23,6 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* A filter used to filter Collections.
*
@ -31,68 +30,72 @@ import java.util.Set;
* @author Paulo Neves
*/
class CollectionFilterer<T> implements Filterer<T> {
//~ Static fields/initializers =====================================================================================
// ~ Static fields/initializers
// =====================================================================================
protected static final Log logger = LogFactory.getLog(CollectionFilterer.class);
protected static final Log logger = LogFactory.getLog(CollectionFilterer.class);
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private final Collection<T> collection;
private final Collection<T> collection;
private final Set<T> removeList;
private final Set<T> removeList;
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
CollectionFilterer(Collection<T> collection) {
this.collection = collection;
CollectionFilterer(Collection<T> collection) {
this.collection = collection;
// We create a Set of objects to be removed from the Collection,
// as ConcurrentModificationException prevents removal during
// iteration, and making a new Collection to be returned is
// problematic as the original Collection implementation passed
// to the method may not necessarily be re-constructable (as
// the Collection(collection) constructor is not guaranteed and
// manually adding may lose sort order or other capabilities)
removeList = new HashSet<T>();
}
// We create a Set of objects to be removed from the Collection,
// as ConcurrentModificationException prevents removal during
// iteration, and making a new Collection to be returned is
// problematic as the original Collection implementation passed
// to the method may not necessarily be re-constructable (as
// the Collection(collection) constructor is not guaranteed and
// manually adding may lose sort order or other capabilities)
removeList = new HashSet<T>();
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
/**
*
* @see org.springframework.security.acls.afterinvocation.Filterer#getFilteredObject()
*/
public Object getFilteredObject() {
// Now the Iterator has ended, remove Objects from Collection
Iterator<T> removeIter = removeList.iterator();
/**
*
* @see org.springframework.security.acls.afterinvocation.Filterer#getFilteredObject()
*/
public Object getFilteredObject() {
// Now the Iterator has ended, remove Objects from Collection
Iterator<T> removeIter = removeList.iterator();
int originalSize = collection.size();
int originalSize = collection.size();
while (removeIter.hasNext()) {
collection.remove(removeIter.next());
}
while (removeIter.hasNext()) {
collection.remove(removeIter.next());
}
if (logger.isDebugEnabled()) {
logger.debug("Original collection contained " + originalSize + " elements; now contains "
+ collection.size() + " elements");
}
if (logger.isDebugEnabled()) {
logger.debug("Original collection contained " + originalSize
+ " elements; now contains " + collection.size() + " elements");
}
return collection;
}
return collection;
}
/**
*
* @see org.springframework.security.acls.afterinvocation.Filterer#iterator()
*/
public Iterator<T> iterator() {
return collection.iterator();
}
/**
*
* @see org.springframework.security.acls.afterinvocation.Filterer#iterator()
*/
public Iterator<T> iterator() {
return collection.iterator();
}
/**
*
* @see org.springframework.security.acls.afterinvocation.Filterer#remove(java.lang.Object)
*/
public void remove(T object) {
removeList.add(object);
}
/**
*
* @see org.springframework.security.acls.afterinvocation.Filterer#remove(java.lang.Object)
*/
public void remove(T object) {
removeList.add(object);
}
}

View File

@ -17,7 +17,6 @@ package org.springframework.security.acls.afterinvocation;
import java.util.Iterator;
/**
* Filterer strategy interface.
*
@ -25,26 +24,27 @@ import java.util.Iterator;
* @author Paulo Neves
*/
interface Filterer<T> extends Iterable<T> {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
/**
* Gets the filtered collection or array.
*
* @return the filtered collection or array
*/
Object getFilteredObject();
/**
* Gets the filtered collection or array.
*
* @return the filtered collection or array
*/
Object getFilteredObject();
/**
* Returns an iterator over the filtered collection or array.
*
* @return an Iterator
*/
Iterator<T> iterator();
/**
* Returns an iterator over the filtered collection or array.
*
* @return an Iterator
*/
Iterator<T> iterator();
/**
* Removes the the given object from the resulting list.
*
* @param object the object to be removed
*/
void remove(T object);
/**
* Removes the the given object from the resulting list.
*
* @param object the object to be removed
*/
void remove(T object);
}

View File

@ -10,63 +10,67 @@ import org.springframework.security.acls.model.Permission;
*/
public abstract class AbstractPermission implements Permission {
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
protected final char code;
protected int mask;
protected final char code;
protected int mask;
//~ Constructors ===================================================================================================
/**
* Sets the permission mask and uses the '*' character to represent active bits when represented as a bit
* pattern string.
*
* @param mask the integer bit mask for the permission
*/
protected AbstractPermission(int mask) {
this.mask = mask;
this.code = '*';
}
// ~ Constructors
// ===================================================================================================
/**
* Sets the permission mask and uses the '*' character to represent active bits when
* represented as a bit pattern string.
*
* @param mask the integer bit mask for the permission
*/
protected AbstractPermission(int mask) {
this.mask = mask;
this.code = '*';
}
/**
* Sets the permission mask and uses the specified character for active bits.
*
* @param mask the integer bit mask for the permission
* @param code the character to print for each active bit in the mask (see {@link Permission#getPattern()})
*/
protected AbstractPermission(int mask, char code) {
this.mask = mask;
this.code = code;
}
/**
* Sets the permission mask and uses the specified character for active bits.
*
* @param mask the integer bit mask for the permission
* @param code the character to print for each active bit in the mask (see
* {@link Permission#getPattern()})
*/
protected AbstractPermission(int mask, char code) {
this.mask = mask;
this.code = code;
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public final boolean equals(Object arg0) {
if (arg0 == null) {
return false;
}
public final boolean equals(Object arg0) {
if (arg0 == null) {
return false;
}
if (!(arg0 instanceof Permission)) {
return false;
}
if (!(arg0 instanceof Permission)) {
return false;
}
Permission rhs = (Permission) arg0;
Permission rhs = (Permission) arg0;
return (this.mask == rhs.getMask());
}
return (this.mask == rhs.getMask());
}
public final int getMask() {
return mask;
}
public final int getMask() {
return mask;
}
public String getPattern() {
return AclFormattingUtils.printBinary(mask, code);
}
public String getPattern() {
return AclFormattingUtils.printBinary(mask, code);
}
public final String toString() {
return this.getClass().getSimpleName() + "[" + getPattern() + "=" + mask + "]";
}
public final String toString() {
return this.getClass().getSimpleName() + "[" + getPattern() + "=" + mask + "]";
}
public final int hashCode() {
return this.mask;
}
public final int hashCode() {
return this.mask;
}
}

View File

@ -24,151 +24,161 @@ import org.springframework.util.Assert;
import java.io.Serializable;
/**
* An immutable default implementation of <code>AccessControlEntry</code>.
*
* @author Ben Alex
*/
public class AccessControlEntryImpl implements AccessControlEntry, AuditableAccessControlEntry {
//~ Instance fields ================================================================================================
public class AccessControlEntryImpl implements AccessControlEntry,
AuditableAccessControlEntry {
// ~ Instance fields
// ================================================================================================
private final Acl acl;
private Permission permission;
private final Serializable id;
private final Sid sid;
private boolean auditFailure = false;
private boolean auditSuccess = false;
private final boolean granting;
private final Acl acl;
private Permission permission;
private final Serializable id;
private final Sid sid;
private boolean auditFailure = false;
private boolean auditSuccess = false;
private final boolean granting;
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
public AccessControlEntryImpl(Serializable id, Acl acl, Sid sid, Permission permission, boolean granting,
boolean auditSuccess, boolean auditFailure) {
Assert.notNull(acl, "Acl required");
Assert.notNull(sid, "Sid required");
Assert.notNull(permission, "Permission required");
this.id = id;
this.acl = acl; // can be null
this.sid = sid;
this.permission = permission;
this.granting = granting;
this.auditSuccess = auditSuccess;
this.auditFailure = auditFailure;
}
public AccessControlEntryImpl(Serializable id, Acl acl, Sid sid,
Permission permission, boolean granting, boolean auditSuccess,
boolean auditFailure) {
Assert.notNull(acl, "Acl required");
Assert.notNull(sid, "Sid required");
Assert.notNull(permission, "Permission required");
this.id = id;
this.acl = acl; // can be null
this.sid = sid;
this.permission = permission;
this.granting = granting;
this.auditSuccess = auditSuccess;
this.auditFailure = auditFailure;
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public boolean equals(Object arg0) {
if (!(arg0 instanceof AccessControlEntryImpl)) {
return false;
}
public boolean equals(Object arg0) {
if (!(arg0 instanceof AccessControlEntryImpl)) {
return false;
}
AccessControlEntryImpl rhs = (AccessControlEntryImpl) arg0;
AccessControlEntryImpl rhs = (AccessControlEntryImpl) arg0;
if (this.acl == null) {
if (rhs.getAcl() != null) {
return false;
}
// Both this.acl and rhs.acl are null and thus equal
} else {
// this.acl is non-null
if (rhs.getAcl() == null) {
return false;
}
if (this.acl == null) {
if (rhs.getAcl() != null) {
return false;
}
// Both this.acl and rhs.acl are null and thus equal
}
else {
// this.acl is non-null
if (rhs.getAcl() == null) {
return false;
}
// Both this.acl and rhs.acl are non-null, so do a comparison
if (this.acl.getObjectIdentity() == null) {
if (rhs.acl.getObjectIdentity() != null) {
return false;
}
// Both this.acl and rhs.acl are null and thus equal
} else {
// Both this.acl.objectIdentity and rhs.acl.objectIdentity are non-null
if (!this.acl.getObjectIdentity().equals(rhs.getAcl().getObjectIdentity())) {
return false;
}
}
}
// Both this.acl and rhs.acl are non-null, so do a comparison
if (this.acl.getObjectIdentity() == null) {
if (rhs.acl.getObjectIdentity() != null) {
return false;
}
// Both this.acl and rhs.acl are null and thus equal
}
else {
// Both this.acl.objectIdentity and rhs.acl.objectIdentity are non-null
if (!this.acl.getObjectIdentity()
.equals(rhs.getAcl().getObjectIdentity())) {
return false;
}
}
}
if (this.id == null) {
if (rhs.id != null) {
return false;
}
// Both this.id and rhs.id are null and thus equal
} else {
// this.id is non-null
if (rhs.id == null) {
return false;
}
if (this.id == null) {
if (rhs.id != null) {
return false;
}
// Both this.id and rhs.id are null and thus equal
}
else {
// this.id is non-null
if (rhs.id == null) {
return false;
}
// Both this.id and rhs.id are non-null
if (!this.id.equals(rhs.id)) {
return false;
}
}
// Both this.id and rhs.id are non-null
if (!this.id.equals(rhs.id)) {
return false;
}
}
if ((this.auditFailure != rhs.isAuditFailure()) || (this.auditSuccess != rhs.isAuditSuccess())
|| (this.granting != rhs.isGranting())
|| !this.permission.equals(rhs.getPermission()) || !this.sid.equals(rhs.getSid())) {
return false;
}
if ((this.auditFailure != rhs.isAuditFailure())
|| (this.auditSuccess != rhs.isAuditSuccess())
|| (this.granting != rhs.isGranting())
|| !this.permission.equals(rhs.getPermission())
|| !this.sid.equals(rhs.getSid())) {
return false;
}
return true;
}
return true;
}
public Acl getAcl() {
return acl;
}
public Acl getAcl() {
return acl;
}
public Serializable getId() {
return id;
}
public Serializable getId() {
return id;
}
public Permission getPermission() {
return permission;
}
public Permission getPermission() {
return permission;
}
public Sid getSid() {
return sid;
}
public Sid getSid() {
return sid;
}
public boolean isAuditFailure() {
return auditFailure;
}
public boolean isAuditFailure() {
return auditFailure;
}
public boolean isAuditSuccess() {
return auditSuccess;
}
public boolean isAuditSuccess() {
return auditSuccess;
}
public boolean isGranting() {
return granting;
}
public boolean isGranting() {
return granting;
}
void setAuditFailure(boolean auditFailure) {
this.auditFailure = auditFailure;
}
void setAuditFailure(boolean auditFailure) {
this.auditFailure = auditFailure;
}
void setAuditSuccess(boolean auditSuccess) {
this.auditSuccess = auditSuccess;
}
void setAuditSuccess(boolean auditSuccess) {
this.auditSuccess = auditSuccess;
}
void setPermission(Permission permission) {
Assert.notNull(permission, "Permission required");
this.permission = permission;
}
void setPermission(Permission permission) {
Assert.notNull(permission, "Permission required");
this.permission = permission;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("AccessControlEntryImpl[");
sb.append("id: ").append(this.id).append("; ");
sb.append("granting: ").append(this.granting).append("; ");
sb.append("sid: ").append(this.sid).append("; ");
sb.append("permission: ").append(this.permission).append("; ");
sb.append("auditSuccess: ").append(this.auditSuccess).append("; ");
sb.append("auditFailure: ").append(this.auditFailure);
sb.append("]");
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("AccessControlEntryImpl[");
sb.append("id: ").append(this.id).append("; ");
sb.append("granting: ").append(this.granting).append("; ");
sb.append("sid: ").append(this.sid).append("; ");
sb.append("permission: ").append(this.permission).append("; ");
sb.append("auditSuccess: ").append(this.auditSuccess).append("; ");
sb.append("auditFailure: ").append(this.auditFailure);
sb.append("]");
return sb.toString();
}
return sb.toString();
}
}

View File

@ -17,7 +17,6 @@ package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Acl;
/**
* Strategy used by {@link AclImpl} to determine whether a principal is permitted to call
* adminstrative methods on the <code>AclImpl</code>.
@ -25,13 +24,15 @@ import org.springframework.security.acls.model.Acl;
* @author Ben Alex
*/
public interface AclAuthorizationStrategy {
//~ Static fields/initializers =====================================================================================
// ~ Static fields/initializers
// =====================================================================================
int CHANGE_OWNERSHIP = 0;
int CHANGE_AUDITING = 1;
int CHANGE_GENERAL = 2;
int CHANGE_OWNERSHIP = 0;
int CHANGE_AUDITING = 1;
int CHANGE_GENERAL = 2;
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
void securityCheck(Acl acl, int changeType);
void securityCheck(Acl acl, int changeType);
}

View File

@ -27,115 +27,124 @@ import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
/**
* Default implementation of {@link AclAuthorizationStrategy}.
* <p>
* Permission will be granted if at least one of the following conditions is true for the current
* principal.
* Permission will be granted if at least one of the following conditions is true for the
* current principal.
* <ul>
* <li> is the owner (as defined by the ACL). </li>
* <li> holds the relevant system-wide {@link GrantedAuthority} injected into the
* constructor. </li>
* <li> has {@link BasePermission#ADMINISTRATION} permission (as defined by the ACL). </li>
* <li>is the owner (as defined by the ACL).</li>
* <li>holds the relevant system-wide {@link GrantedAuthority} injected into the
* constructor.</li>
* <li>has {@link BasePermission#ADMINISTRATION} permission (as defined by the ACL).</li>
* </ul>
*
* @author Ben Alex
*/
public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private final GrantedAuthority gaGeneralChanges;
private final GrantedAuthority gaModifyAuditing;
private final GrantedAuthority gaTakeOwnership;
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
private final GrantedAuthority gaGeneralChanges;
private final GrantedAuthority gaModifyAuditing;
private final GrantedAuthority gaTakeOwnership;
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
/**
* Constructor. The only mandatory parameter relates to the system-wide {@link GrantedAuthority} instances that
* can be held to always permit ACL changes.
*
* @param auths the <code>GrantedAuthority</code>s that have
* special permissions (index 0 is the authority needed to change
* ownership, index 1 is the authority needed to modify auditing details,
* index 2 is the authority needed to change other ACL and ACE details) (required)
* <p>
* Alternatively, a single value can be supplied for all three permissions.
*/
public AclAuthorizationStrategyImpl(GrantedAuthority... auths) {
Assert.isTrue(auths != null && (auths.length == 3 || auths.length == 1),
"One or three GrantedAuthority instances required");
if (auths.length == 3) {
gaTakeOwnership = auths[0];
gaModifyAuditing = auths[1];
gaGeneralChanges = auths[2];
} else {
gaTakeOwnership = gaModifyAuditing = gaGeneralChanges = auths[0];
}
}
/**
* Constructor. The only mandatory parameter relates to the system-wide
* {@link GrantedAuthority} instances that can be held to always permit ACL changes.
*
* @param auths the <code>GrantedAuthority</code>s that have special permissions
* (index 0 is the authority needed to change ownership, index 1 is the authority
* needed to modify auditing details, index 2 is the authority needed to change other
* ACL and ACE details) (required)
* <p>
* Alternatively, a single value can be supplied for all three permissions.
*/
public AclAuthorizationStrategyImpl(GrantedAuthority... auths) {
Assert.isTrue(auths != null && (auths.length == 3 || auths.length == 1),
"One or three GrantedAuthority instances required");
if (auths.length == 3) {
gaTakeOwnership = auths[0];
gaModifyAuditing = auths[1];
gaGeneralChanges = auths[2];
}
else {
gaTakeOwnership = gaModifyAuditing = gaGeneralChanges = auths[0];
}
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public void securityCheck(Acl acl, int changeType) {
if ((SecurityContextHolder.getContext() == null)
|| (SecurityContextHolder.getContext().getAuthentication() == null)
|| !SecurityContextHolder.getContext().getAuthentication().isAuthenticated()) {
throw new AccessDeniedException("Authenticated principal required to operate with ACLs");
}
public void securityCheck(Acl acl, int changeType) {
if ((SecurityContextHolder.getContext() == null)
|| (SecurityContextHolder.getContext().getAuthentication() == null)
|| !SecurityContextHolder.getContext().getAuthentication()
.isAuthenticated()) {
throw new AccessDeniedException(
"Authenticated principal required to operate with ACLs");
}
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
// Check if authorized by virtue of ACL ownership
Sid currentUser = createCurrentUser(authentication);
// Check if authorized by virtue of ACL ownership
Sid currentUser = createCurrentUser(authentication);
if (currentUser.equals(acl.getOwner())
&& ((changeType == CHANGE_GENERAL) || (changeType == CHANGE_OWNERSHIP))) {
return;
}
if (currentUser.equals(acl.getOwner())
&& ((changeType == CHANGE_GENERAL) || (changeType == CHANGE_OWNERSHIP))) {
return;
}
// Not authorized by ACL ownership; try via adminstrative permissions
GrantedAuthority requiredAuthority;
// Not authorized by ACL ownership; try via adminstrative permissions
GrantedAuthority requiredAuthority;
if (changeType == CHANGE_AUDITING) {
requiredAuthority = this.gaModifyAuditing;
} else if (changeType == CHANGE_GENERAL) {
requiredAuthority = this.gaGeneralChanges;
} else if (changeType == CHANGE_OWNERSHIP) {
requiredAuthority = this.gaTakeOwnership;
} else {
throw new IllegalArgumentException("Unknown change type");
}
if (changeType == CHANGE_AUDITING) {
requiredAuthority = this.gaModifyAuditing;
}
else if (changeType == CHANGE_GENERAL) {
requiredAuthority = this.gaGeneralChanges;
}
else if (changeType == CHANGE_OWNERSHIP) {
requiredAuthority = this.gaTakeOwnership;
}
else {
throw new IllegalArgumentException("Unknown change type");
}
// Iterate this principal's authorities to determine right
if (authentication.getAuthorities().contains(requiredAuthority)) {
return;
}
// Iterate this principal's authorities to determine right
if (authentication.getAuthorities().contains(requiredAuthority)) {
return;
}
// Try to get permission via ACEs within the ACL
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
// Try to get permission via ACEs within the ACL
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
if (acl.isGranted(Arrays.asList(BasePermission.ADMINISTRATION), sids, false)) {
return;
}
if (acl.isGranted(Arrays.asList(BasePermission.ADMINISTRATION), sids, false)) {
return;
}
throw new AccessDeniedException(
"Principal does not have required ACL permissions to perform requested operation");
}
throw new AccessDeniedException(
"Principal does not have required ACL permissions to perform requested operation");
}
/**
* Creates a principal-like sid from the authentication information.
*
* @param authentication the authentication information that can provide principal and thus the sid's id will be
* dependant on the value inside
* @return a sid with the ID taken from the authentication information
*/
protected Sid createCurrentUser(Authentication authentication) {
return new PrincipalSid(authentication);
}
/**
* Creates a principal-like sid from the authentication information.
*
* @param authentication the authentication information that can provide principal and
* thus the sid's id will be dependant on the value inside
* @return a sid with the ID taken from the authentication information
*/
protected Sid createCurrentUser(Authentication authentication) {
return new PrincipalSid(authentication);
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required");
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required");
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
}

View File

@ -17,7 +17,6 @@ package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Permission;
import org.springframework.util.Assert;
/**
* Utility methods for displaying ACL information.
*
@ -25,83 +24,88 @@ import org.springframework.util.Assert;
*/
public abstract class AclFormattingUtils {
public static String demergePatterns(String original, String removeBits) {
Assert.notNull(original, "Original string required");
Assert.notNull(removeBits, "Bits To Remove string required");
Assert.isTrue(original.length() == removeBits.length(),
"Original and Bits To Remove strings must be identical length");
public static String demergePatterns(String original, String removeBits) {
Assert.notNull(original, "Original string required");
Assert.notNull(removeBits, "Bits To Remove string required");
Assert.isTrue(original.length() == removeBits.length(),
"Original and Bits To Remove strings must be identical length");
char[] replacement = new char[original.length()];
char[] replacement = new char[original.length()];
for (int i = 0; i < original.length(); i++) {
if (removeBits.charAt(i) == Permission.RESERVED_OFF) {
replacement[i] = original.charAt(i);
} else {
replacement[i] = Permission.RESERVED_OFF;
}
}
for (int i = 0; i < original.length(); i++) {
if (removeBits.charAt(i) == Permission.RESERVED_OFF) {
replacement[i] = original.charAt(i);
}
else {
replacement[i] = Permission.RESERVED_OFF;
}
}
return new String(replacement);
}
return new String(replacement);
}
public static String mergePatterns(String original, String extraBits) {
Assert.notNull(original, "Original string required");
Assert.notNull(extraBits, "Extra Bits string required");
Assert.isTrue(original.length() == extraBits.length(),
"Original and Extra Bits strings must be identical length");
public static String mergePatterns(String original, String extraBits) {
Assert.notNull(original, "Original string required");
Assert.notNull(extraBits, "Extra Bits string required");
Assert.isTrue(original.length() == extraBits.length(),
"Original and Extra Bits strings must be identical length");
char[] replacement = new char[extraBits.length()];
char[] replacement = new char[extraBits.length()];
for (int i = 0; i < extraBits.length(); i++) {
if (extraBits.charAt(i) == Permission.RESERVED_OFF) {
replacement[i] = original.charAt(i);
} else {
replacement[i] = extraBits.charAt(i);
}
}
for (int i = 0; i < extraBits.length(); i++) {
if (extraBits.charAt(i) == Permission.RESERVED_OFF) {
replacement[i] = original.charAt(i);
}
else {
replacement[i] = extraBits.charAt(i);
}
}
return new String(replacement);
}
return new String(replacement);
}
/**
* Returns a representation of the active bits in the presented mask, with each active bit being denoted by
* character '*'.
* <p>
* Inactive bits will be denoted by character {@link Permission#RESERVED_OFF}.
*
* @param i the integer bit mask to print the active bits for
*
* @return a 32-character representation of the bit mask
*/
public static String printBinary(int i) {
return printBinary(i, '*', Permission.RESERVED_OFF);
}
/**
* Returns a representation of the active bits in the presented mask, with each active
* bit being denoted by character '*'.
* <p>
* Inactive bits will be denoted by character {@link Permission#RESERVED_OFF}.
*
* @param i the integer bit mask to print the active bits for
*
* @return a 32-character representation of the bit mask
*/
public static String printBinary(int i) {
return printBinary(i, '*', Permission.RESERVED_OFF);
}
/**
* Returns a representation of the active bits in the presented mask, with each active bit being denoted by
* the passed character.
* <p>
* Inactive bits will be denoted by character {@link Permission#RESERVED_OFF}.
*
* @param mask the integer bit mask to print the active bits for
* @param code the character to print when an active bit is detected
*
* @return a 32-character representation of the bit mask
*/
public static String printBinary(int mask, char code) {
Assert.doesNotContain(Character.toString(code), Character.toString(Permission.RESERVED_ON),
Permission.RESERVED_ON + " is a reserved character code");
Assert.doesNotContain(Character.toString(code), Character.toString(Permission.RESERVED_OFF),
Permission.RESERVED_OFF + " is a reserved character code");
/**
* Returns a representation of the active bits in the presented mask, with each active
* bit being denoted by the passed character.
* <p>
* Inactive bits will be denoted by character {@link Permission#RESERVED_OFF}.
*
* @param mask the integer bit mask to print the active bits for
* @param code the character to print when an active bit is detected
*
* @return a 32-character representation of the bit mask
*/
public static String printBinary(int mask, char code) {
Assert.doesNotContain(Character.toString(code),
Character.toString(Permission.RESERVED_ON), Permission.RESERVED_ON
+ " is a reserved character code");
Assert.doesNotContain(Character.toString(code),
Character.toString(Permission.RESERVED_OFF), Permission.RESERVED_OFF
+ " is a reserved character code");
return printBinary(mask, Permission.RESERVED_ON, Permission.RESERVED_OFF).replace(Permission.RESERVED_ON, code);
}
return printBinary(mask, Permission.RESERVED_ON, Permission.RESERVED_OFF)
.replace(Permission.RESERVED_ON, code);
}
private static String printBinary(int i, char on, char off) {
String s = Integer.toBinaryString(i);
String pattern = Permission.THIRTY_TWO_RESERVED_OFF;
String temp2 = pattern.substring(0, pattern.length() - s.length()) + s;
private static String printBinary(int i, char on, char off) {
String s = Integer.toBinaryString(i);
String pattern = Permission.THIRTY_TWO_RESERVED_OFF;
String temp2 = pattern.substring(0, pattern.length() - s.length()) + s;
return temp2.replace('0', off).replace('1', on);
}
return temp2.replace('0', off).replace('1', on);
}
}

View File

@ -31,297 +31,333 @@ import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.UnloadedSidException;
import org.springframework.util.Assert;
/**
* Base implementation of <code>Acl</code>.
*
* @author Ben Alex
*/
public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private Acl parentAcl;
private transient AclAuthorizationStrategy aclAuthorizationStrategy;
private transient PermissionGrantingStrategy permissionGrantingStrategy;
private final List<AccessControlEntry> aces = new ArrayList<AccessControlEntry>();
private ObjectIdentity objectIdentity;
private Serializable id;
private Sid owner; // OwnershipAcl
private List<Sid> loadedSids = null; // includes all SIDs the WHERE clause covered, even if there was no ACE for a SID
private boolean entriesInheriting = true;
private Acl parentAcl;
private transient AclAuthorizationStrategy aclAuthorizationStrategy;
private transient PermissionGrantingStrategy permissionGrantingStrategy;
private final List<AccessControlEntry> aces = new ArrayList<AccessControlEntry>();
private ObjectIdentity objectIdentity;
private Serializable id;
private Sid owner; // OwnershipAcl
private List<Sid> loadedSids = null; // includes all SIDs the WHERE clause covered,
// even if there was no ACE for a SID
private boolean entriesInheriting = true;
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
/**
* Minimal constructor, which should be used {@link
* org.springframework.security.acls.model.MutableAclService#createAcl(ObjectIdentity)}.
*
* @param objectIdentity the object identity this ACL relates to (required)
* @param id the primary key assigned to this ACL (required)
* @param aclAuthorizationStrategy authorization strategy (required)
* @param auditLogger audit logger (required)
*/
public AclImpl(ObjectIdentity objectIdentity, Serializable id, AclAuthorizationStrategy aclAuthorizationStrategy,
AuditLogger auditLogger) {
Assert.notNull(objectIdentity, "Object Identity required");
Assert.notNull(id, "Id required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
Assert.notNull(auditLogger, "AuditLogger required");
this.objectIdentity = objectIdentity;
this.id = id;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
this.permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(auditLogger);
}
/**
* Minimal constructor, which should be used
* {@link org.springframework.security.acls.model.MutableAclService#createAcl(ObjectIdentity)}
* .
*
* @param objectIdentity the object identity this ACL relates to (required)
* @param id the primary key assigned to this ACL (required)
* @param aclAuthorizationStrategy authorization strategy (required)
* @param auditLogger audit logger (required)
*/
public AclImpl(ObjectIdentity objectIdentity, Serializable id,
AclAuthorizationStrategy aclAuthorizationStrategy, AuditLogger auditLogger) {
Assert.notNull(objectIdentity, "Object Identity required");
Assert.notNull(id, "Id required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
Assert.notNull(auditLogger, "AuditLogger required");
this.objectIdentity = objectIdentity;
this.id = id;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
this.permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(
auditLogger);
}
/**
* Full constructor, which should be used by persistence tools that do not
* provide field-level access features.
*
* @param objectIdentity the object identity this ACL relates to
* @param id the primary key assigned to this ACL
* @param aclAuthorizationStrategy authorization strategy
* @param grantingStrategy the {@code PermissionGrantingStrategy} which will be used by the {@code isGranted()} method
* @param parentAcl the parent (may be may be {@code null})
* @param loadedSids the loaded SIDs if only a subset were loaded (may be {@code null})
* @param entriesInheriting if ACEs from the parent should inherit into
* this ACL
* @param owner the owner (required)
*/
public AclImpl(ObjectIdentity objectIdentity, Serializable id, AclAuthorizationStrategy aclAuthorizationStrategy,
PermissionGrantingStrategy grantingStrategy, Acl parentAcl, List<Sid> loadedSids, boolean entriesInheriting, Sid owner) {
Assert.notNull(objectIdentity, "Object Identity required");
Assert.notNull(id, "Id required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
Assert.notNull(owner, "Owner required");
/**
* Full constructor, which should be used by persistence tools that do not provide
* field-level access features.
*
* @param objectIdentity the object identity this ACL relates to
* @param id the primary key assigned to this ACL
* @param aclAuthorizationStrategy authorization strategy
* @param grantingStrategy the {@code PermissionGrantingStrategy} which will be used
* by the {@code isGranted()} method
* @param parentAcl the parent (may be may be {@code null})
* @param loadedSids the loaded SIDs if only a subset were loaded (may be {@code null}
* )
* @param entriesInheriting if ACEs from the parent should inherit into this ACL
* @param owner the owner (required)
*/
public AclImpl(ObjectIdentity objectIdentity, Serializable id,
AclAuthorizationStrategy aclAuthorizationStrategy,
PermissionGrantingStrategy grantingStrategy, Acl parentAcl,
List<Sid> loadedSids, boolean entriesInheriting, Sid owner) {
Assert.notNull(objectIdentity, "Object Identity required");
Assert.notNull(id, "Id required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
Assert.notNull(owner, "Owner required");
this.objectIdentity = objectIdentity;
this.id = id;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
this.parentAcl = parentAcl; // may be null
this.loadedSids = loadedSids; // may be null
this.entriesInheriting = entriesInheriting;
this.owner = owner;
this.permissionGrantingStrategy = grantingStrategy;
}
this.objectIdentity = objectIdentity;
this.id = id;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
this.parentAcl = parentAcl; // may be null
this.loadedSids = loadedSids; // may be null
this.entriesInheriting = entriesInheriting;
this.owner = owner;
this.permissionGrantingStrategy = grantingStrategy;
}
/**
* Private no-argument constructor for use by reflection-based persistence
* tools along with field-level access.
*/
@SuppressWarnings("unused")
private AclImpl() {}
/**
* Private no-argument constructor for use by reflection-based persistence tools along
* with field-level access.
*/
@SuppressWarnings("unused")
private AclImpl() {
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public void deleteAce(int aceIndex) throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
verifyAceIndexExists(aceIndex);
public void deleteAce(int aceIndex) throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_GENERAL);
verifyAceIndexExists(aceIndex);
synchronized (aces) {
this.aces.remove(aceIndex);
}
}
synchronized (aces) {
this.aces.remove(aceIndex);
}
}
private void verifyAceIndexExists(int aceIndex) {
if (aceIndex < 0) {
throw new NotFoundException("aceIndex must be greater than or equal to zero");
}
if (aceIndex >= this.aces.size()) {
throw new NotFoundException("aceIndex must refer to an index of the AccessControlEntry list. " +
"List size is " + aces.size() + ", index was " + aceIndex);
}
}
private void verifyAceIndexExists(int aceIndex) {
if (aceIndex < 0) {
throw new NotFoundException("aceIndex must be greater than or equal to zero");
}
if (aceIndex >= this.aces.size()) {
throw new NotFoundException(
"aceIndex must refer to an index of the AccessControlEntry list. "
+ "List size is " + aces.size() + ", index was " + aceIndex);
}
}
public void insertAce(int atIndexLocation, Permission permission, Sid sid, boolean granting) throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.notNull(permission, "Permission required");
Assert.notNull(sid, "Sid required");
if (atIndexLocation < 0) {
throw new NotFoundException("atIndexLocation must be greater than or equal to zero");
}
if (atIndexLocation > this.aces.size()) {
throw new NotFoundException("atIndexLocation must be less than or equal to the size of the AccessControlEntry collection");
}
public void insertAce(int atIndexLocation, Permission permission, Sid sid,
boolean granting) throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.notNull(permission, "Permission required");
Assert.notNull(sid, "Sid required");
if (atIndexLocation < 0) {
throw new NotFoundException(
"atIndexLocation must be greater than or equal to zero");
}
if (atIndexLocation > this.aces.size()) {
throw new NotFoundException(
"atIndexLocation must be less than or equal to the size of the AccessControlEntry collection");
}
AccessControlEntryImpl ace = new AccessControlEntryImpl(null, this, sid, permission, granting, false, false);
AccessControlEntryImpl ace = new AccessControlEntryImpl(null, this, sid,
permission, granting, false, false);
synchronized (aces) {
this.aces.add(atIndexLocation, ace);
}
}
synchronized (aces) {
this.aces.add(atIndexLocation, ace);
}
}
public List<AccessControlEntry> getEntries() {
// Can safely return AccessControlEntry directly, as they're immutable outside the ACL package
return new ArrayList<AccessControlEntry>(aces);
}
public List<AccessControlEntry> getEntries() {
// Can safely return AccessControlEntry directly, as they're immutable outside the
// ACL package
return new ArrayList<AccessControlEntry>(aces);
}
public Serializable getId() {
return this.id;
}
public Serializable getId() {
return this.id;
}
public ObjectIdentity getObjectIdentity() {
return objectIdentity;
}
public ObjectIdentity getObjectIdentity() {
return objectIdentity;
}
public boolean isEntriesInheriting() {
return entriesInheriting;
}
public boolean isEntriesInheriting() {
return entriesInheriting;
}
/**
* Delegates to the {@link PermissionGrantingStrategy}.
*
* @throws UnloadedSidException if the passed SIDs are unknown to this ACL because the ACL was only loaded for a
* subset of SIDs
* @see DefaultPermissionGrantingStrategy
*/
public boolean isGranted(List<Permission> permission, List<Sid> sids, boolean administrativeMode)
throws NotFoundException, UnloadedSidException {
Assert.notEmpty(permission, "Permissions required");
Assert.notEmpty(sids, "SIDs required");
/**
* Delegates to the {@link PermissionGrantingStrategy}.
*
* @throws UnloadedSidException if the passed SIDs are unknown to this ACL because the
* ACL was only loaded for a subset of SIDs
* @see DefaultPermissionGrantingStrategy
*/
public boolean isGranted(List<Permission> permission, List<Sid> sids,
boolean administrativeMode) throws NotFoundException, UnloadedSidException {
Assert.notEmpty(permission, "Permissions required");
Assert.notEmpty(sids, "SIDs required");
if (!this.isSidLoaded(sids)) {
throw new UnloadedSidException("ACL was not loaded for one or more SID");
}
if (!this.isSidLoaded(sids)) {
throw new UnloadedSidException("ACL was not loaded for one or more SID");
}
return permissionGrantingStrategy.isGranted(this, permission, sids, administrativeMode);
}
return permissionGrantingStrategy.isGranted(this, permission, sids,
administrativeMode);
}
public boolean isSidLoaded(List<Sid> sids) {
// If loadedSides is null, this indicates all SIDs were loaded
// Also return true if the caller didn't specify a SID to find
if ((this.loadedSids == null) || (sids == null) || (sids.size() == 0)) {
return true;
}
public boolean isSidLoaded(List<Sid> sids) {
// If loadedSides is null, this indicates all SIDs were loaded
// Also return true if the caller didn't specify a SID to find
if ((this.loadedSids == null) || (sids == null) || (sids.size() == 0)) {
return true;
}
// This ACL applies to a SID subset only. Iterate to check it applies.
for (Sid sid: sids) {
boolean found = false;
// This ACL applies to a SID subset only. Iterate to check it applies.
for (Sid sid : sids) {
boolean found = false;
for (Sid loadedSid : loadedSids) {
if (sid.equals(loadedSid)) {
// this SID is OK
found = true;
for (Sid loadedSid : loadedSids) {
if (sid.equals(loadedSid)) {
// this SID is OK
found = true;
break; // out of loadedSids for loop
}
}
break; // out of loadedSids for loop
}
}
if (!found) {
return false;
}
}
if (!found) {
return false;
}
}
return true;
}
return true;
}
public void setEntriesInheriting(boolean entriesInheriting) {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
this.entriesInheriting = entriesInheriting;
}
public void setEntriesInheriting(boolean entriesInheriting) {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_GENERAL);
this.entriesInheriting = entriesInheriting;
}
public void setOwner(Sid newOwner) {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
Assert.notNull(newOwner, "Owner required");
this.owner = newOwner;
}
public void setOwner(Sid newOwner) {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_OWNERSHIP);
Assert.notNull(newOwner, "Owner required");
this.owner = newOwner;
}
public Sid getOwner() {
return this.owner;
}
public Sid getOwner() {
return this.owner;
}
public void setParent(Acl newParent) {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.isTrue(newParent == null || !newParent.equals(this), "Cannot be the parent of yourself");
this.parentAcl = newParent;
}
public void setParent(Acl newParent) {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.isTrue(newParent == null || !newParent.equals(this),
"Cannot be the parent of yourself");
this.parentAcl = newParent;
}
public Acl getParentAcl() {
return parentAcl;
}
public Acl getParentAcl() {
return parentAcl;
}
public void updateAce(int aceIndex, Permission permission)
throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_GENERAL);
verifyAceIndexExists(aceIndex);
public void updateAce(int aceIndex, Permission permission) throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_GENERAL);
verifyAceIndexExists(aceIndex);
synchronized (aces) {
AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(aceIndex);
ace.setPermission(permission);
}
}
synchronized (aces) {
AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(aceIndex);
ace.setPermission(permission);
}
}
public void updateAuditing(int aceIndex, boolean auditSuccess, boolean auditFailure) {
aclAuthorizationStrategy.securityCheck(this, AclAuthorizationStrategy.CHANGE_AUDITING);
verifyAceIndexExists(aceIndex);
public void updateAuditing(int aceIndex, boolean auditSuccess, boolean auditFailure) {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_AUDITING);
verifyAceIndexExists(aceIndex);
synchronized (aces) {
AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(aceIndex);
ace.setAuditSuccess(auditSuccess);
ace.setAuditFailure(auditFailure);
}
}
synchronized (aces) {
AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(aceIndex);
ace.setAuditSuccess(auditSuccess);
ace.setAuditFailure(auditFailure);
}
}
public boolean equals(Object obj) {
if (obj instanceof AclImpl) {
AclImpl rhs = (AclImpl) obj;
if (this.aces.equals(rhs.aces)) {
if ((this.parentAcl == null && rhs.parentAcl == null) || (this.parentAcl !=null && this.parentAcl.equals(rhs.parentAcl))) {
if ((this.objectIdentity == null && rhs.objectIdentity == null) || (this.objectIdentity != null && this.objectIdentity.equals(rhs.objectIdentity))) {
if ((this.id == null && rhs.id == null) || (this.id != null && this.id.equals(rhs.id))) {
if ((this.owner == null && rhs.owner == null) || (this.owner != null && this.owner.equals(rhs.owner))) {
if (this.entriesInheriting == rhs.entriesInheriting) {
if ((this.loadedSids == null && rhs.loadedSids == null)) {
return true;
}
if (this.loadedSids != null && (this.loadedSids.size() == rhs.loadedSids.size())) {
for (int i = 0; i < this.loadedSids.size(); i++) {
if (!this.loadedSids.get(i).equals(rhs.loadedSids.get(i))) {
return false;
}
}
return true;
}
}
}
}
}
}
}
}
return false;
}
public boolean equals(Object obj) {
if (obj instanceof AclImpl) {
AclImpl rhs = (AclImpl) obj;
if (this.aces.equals(rhs.aces)) {
if ((this.parentAcl == null && rhs.parentAcl == null)
|| (this.parentAcl != null && this.parentAcl
.equals(rhs.parentAcl))) {
if ((this.objectIdentity == null && rhs.objectIdentity == null)
|| (this.objectIdentity != null && this.objectIdentity
.equals(rhs.objectIdentity))) {
if ((this.id == null && rhs.id == null)
|| (this.id != null && this.id.equals(rhs.id))) {
if ((this.owner == null && rhs.owner == null)
|| (this.owner != null && this.owner
.equals(rhs.owner))) {
if (this.entriesInheriting == rhs.entriesInheriting) {
if ((this.loadedSids == null && rhs.loadedSids == null)) {
return true;
}
if (this.loadedSids != null
&& (this.loadedSids.size() == rhs.loadedSids
.size())) {
for (int i = 0; i < this.loadedSids.size(); i++) {
if (!this.loadedSids.get(i).equals(
rhs.loadedSids.get(i))) {
return false;
}
}
return true;
}
}
}
}
}
}
}
}
return false;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("AclImpl[");
sb.append("id: ").append(this.id).append("; ");
sb.append("objectIdentity: ").append(this.objectIdentity).append("; ");
sb.append("owner: ").append(this.owner).append("; ");
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("AclImpl[");
sb.append("id: ").append(this.id).append("; ");
sb.append("objectIdentity: ").append(this.objectIdentity).append("; ");
sb.append("owner: ").append(this.owner).append("; ");
int count = 0;
int count = 0;
for (AccessControlEntry ace : aces) {
count++;
for (AccessControlEntry ace : aces) {
count++;
if (count == 1) {
sb.append("\n");
}
if (count == 1) {
sb.append("\n");
}
sb.append(ace).append("\n");
}
sb.append(ace).append("\n");
}
if (count == 0) {
sb.append("no ACEs; ");
}
if (count == 0) {
sb.append("no ACEs; ");
}
sb.append("inheriting: ").append(this.entriesInheriting).append("; ");
sb.append("parent: ").append((this.parentAcl == null) ? "Null" : this.parentAcl.getObjectIdentity().toString());
sb.append("; ");
sb.append("aclAuthorizationStrategy: ").append(this.aclAuthorizationStrategy).append("; ");
sb.append("permissionGrantingStrategy: ").append(this.permissionGrantingStrategy);
sb.append("]");
sb.append("inheriting: ").append(this.entriesInheriting).append("; ");
sb.append("parent: ").append(
(this.parentAcl == null) ? "Null" : this.parentAcl.getObjectIdentity()
.toString());
sb.append("; ");
sb.append("aclAuthorizationStrategy: ").append(this.aclAuthorizationStrategy)
.append("; ");
sb.append("permissionGrantingStrategy: ").append(this.permissionGrantingStrategy);
sb.append("]");
return sb.toString();
}
return sb.toString();
}
}

View File

@ -16,7 +16,6 @@ package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.AccessControlEntry;
/**
* Used by <code>AclImpl</code> to log audit events.
*
@ -24,7 +23,8 @@ import org.springframework.security.acls.model.AccessControlEntry;
*
*/
public interface AuditLogger {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
void logIfNeeded(boolean granted, AccessControlEntry ace);
void logIfNeeded(boolean granted, AccessControlEntry ace);
}

View File

@ -16,7 +16,6 @@ package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Permission;
/**
* A set of standard permissions.
*
@ -28,17 +27,17 @@ import org.springframework.security.acls.model.Permission;
* @author Ben Alex
*/
public class BasePermission extends AbstractPermission {
public static final Permission READ = new BasePermission(1 << 0, 'R'); // 1
public static final Permission WRITE = new BasePermission(1 << 1, 'W'); // 2
public static final Permission CREATE = new BasePermission(1 << 2, 'C'); // 4
public static final Permission DELETE = new BasePermission(1 << 3, 'D'); // 8
public static final Permission ADMINISTRATION = new BasePermission(1 << 4, 'A'); // 16
public static final Permission READ = new BasePermission(1 << 0, 'R'); // 1
public static final Permission WRITE = new BasePermission(1 << 1, 'W'); // 2
public static final Permission CREATE = new BasePermission(1 << 2, 'C'); // 4
public static final Permission DELETE = new BasePermission(1 << 3, 'D'); // 8
public static final Permission ADMINISTRATION = new BasePermission(1 << 4, 'A'); // 16
protected BasePermission(int mask) {
super(mask);
}
protected BasePermission(int mask) {
super(mask);
}
protected BasePermission(int mask, char code) {
super(mask, code);
}
protected BasePermission(int mask, char code) {
super(mask, code);
}
}

View File

@ -19,26 +19,27 @@ import org.springframework.security.acls.model.AuditableAccessControlEntry;
import org.springframework.util.Assert;
/**
* A basic implementation of {@link AuditLogger}.
*
* @author Ben Alex
*/
public class ConsoleAuditLogger implements AuditLogger {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public void logIfNeeded(boolean granted, AccessControlEntry ace) {
Assert.notNull(ace, "AccessControlEntry required");
public void logIfNeeded(boolean granted, AccessControlEntry ace) {
Assert.notNull(ace, "AccessControlEntry required");
if (ace instanceof AuditableAccessControlEntry) {
AuditableAccessControlEntry auditableAce = (AuditableAccessControlEntry) ace;
if (ace instanceof AuditableAccessControlEntry) {
AuditableAccessControlEntry auditableAce = (AuditableAccessControlEntry) ace;
if (granted && auditableAce.isAuditSuccess()) {
System.out.println("GRANTED due to ACE: " + ace);
} else if (!granted && auditableAce.isAuditFailure()) {
System.out.println("DENIED due to ACE: " + ace);
}
}
}
if (granted && auditableAce.isAuditSuccess()) {
System.out.println("GRANTED due to ACE: " + ace);
}
else if (!granted && auditableAce.isAuditFailure()) {
System.out.println("DENIED due to ACE: " + ace);
}
}
}
}

View File

@ -16,45 +16,49 @@ package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Permission;
/**
* Represents a <code>Permission</code> that is constructed at runtime from other permissions.
* Represents a <code>Permission</code> that is constructed at runtime from other
* permissions.
*
* <p>Methods return <code>this</code>, in order to facilitate method chaining.</p>
* <p>
* Methods return <code>this</code>, in order to facilitate method chaining.
* </p>
*
* @author Ben Alex
*/
public class CumulativePermission extends AbstractPermission {
private String pattern = THIRTY_TWO_RESERVED_OFF;
private String pattern = THIRTY_TWO_RESERVED_OFF;
public CumulativePermission() {
super(0, ' ');
}
public CumulativePermission() {
super(0, ' ');
}
public CumulativePermission clear(Permission permission) {
this.mask &= ~permission.getMask();
this.pattern = AclFormattingUtils.demergePatterns(this.pattern, permission.getPattern());
public CumulativePermission clear(Permission permission) {
this.mask &= ~permission.getMask();
this.pattern = AclFormattingUtils.demergePatterns(this.pattern,
permission.getPattern());
return this;
}
return this;
}
public CumulativePermission clear() {
this.mask = 0;
this.pattern = THIRTY_TWO_RESERVED_OFF;
public CumulativePermission clear() {
this.mask = 0;
this.pattern = THIRTY_TWO_RESERVED_OFF;
return this;
}
return this;
}
public CumulativePermission set(Permission permission) {
this.mask |= permission.getMask();
this.pattern = AclFormattingUtils.mergePatterns(this.pattern, permission.getPattern());
public CumulativePermission set(Permission permission) {
this.mask |= permission.getMask();
this.pattern = AclFormattingUtils.mergePatterns(this.pattern,
permission.getPattern());
return this;
}
return this;
}
public String getPattern() {
return this.pattern;
}
public String getPattern() {
return this.pattern;
}
}

View File

@ -13,8 +13,8 @@ import org.springframework.util.Assert;
/**
* Default implementation of {@link PermissionFactory}.
* <p>
* Used as a strategy by classes which wish to map integer masks and permission names to <tt>Permission</tt>
* instances for use with the ACL implementation.
* Used as a strategy by classes which wish to map integer masks and permission names to
* <tt>Permission</tt> instances for use with the ACL implementation.
* <p>
* Maintains a registry of permission names and masks to <tt>Permission</tt> instances.
*
@ -23,125 +23,131 @@ import org.springframework.util.Assert;
* @since 2.0.3
*/
public class DefaultPermissionFactory implements PermissionFactory {
private final Map<Integer, Permission> registeredPermissionsByInteger = new HashMap<Integer, Permission>();
private final Map<String, Permission> registeredPermissionsByName = new HashMap<String, Permission>();
private final Map<Integer, Permission> registeredPermissionsByInteger = new HashMap<Integer, Permission>();
private final Map<String, Permission> registeredPermissionsByName = new HashMap<String, Permission>();
/**
* Registers the <tt>Permission</tt> fields from the <tt>BasePermission</tt> class.
*/
public DefaultPermissionFactory() {
registerPublicPermissions(BasePermission.class);
}
/**
* Registers the <tt>Permission</tt> fields from the <tt>BasePermission</tt> class.
*/
public DefaultPermissionFactory() {
registerPublicPermissions(BasePermission.class);
}
/**
* Registers the <tt>Permission</tt> fields from the supplied class.
*/
public DefaultPermissionFactory(Class<? extends Permission> permissionClass) {
registerPublicPermissions(permissionClass);
}
/**
* Registers the <tt>Permission</tt> fields from the supplied class.
*/
public DefaultPermissionFactory(Class<? extends Permission> permissionClass) {
registerPublicPermissions(permissionClass);
}
/**
* Registers a map of named <tt>Permission</tt> instances.
*
* @param namedPermissions the map of <tt>Permission</tt>s, keyed by name.
*/
public DefaultPermissionFactory(Map<String, ? extends Permission> namedPermissions) {
for (String name : namedPermissions.keySet()) {
registerPermission(namedPermissions.get(name), name);
}
}
/**
* Registers a map of named <tt>Permission</tt> instances.
*
* @param namedPermissions the map of <tt>Permission</tt>s, keyed by name.
*/
public DefaultPermissionFactory(Map<String, ? extends Permission> namedPermissions) {
for (String name : namedPermissions.keySet()) {
registerPermission(namedPermissions.get(name), name);
}
}
/**
* Registers the public static fields of type {@link Permission} for a give class.
* <p>
* These permissions will be registered under the name of the field. See {@link BasePermission}
* for an example.
*
* @param clazz a {@link Permission} class with public static fields to register
*/
protected void registerPublicPermissions(Class<? extends Permission> clazz) {
Assert.notNull(clazz, "Class required");
/**
* Registers the public static fields of type {@link Permission} for a give class.
* <p>
* These permissions will be registered under the name of the field. See
* {@link BasePermission} for an example.
*
* @param clazz a {@link Permission} class with public static fields to register
*/
protected void registerPublicPermissions(Class<? extends Permission> clazz) {
Assert.notNull(clazz, "Class required");
Field[] fields = clazz.getFields();
Field[] fields = clazz.getFields();
for (Field field : fields) {
try {
Object fieldValue = field.get(null);
for (Field field : fields) {
try {
Object fieldValue = field.get(null);
if (Permission.class.isAssignableFrom(fieldValue.getClass())) {
// Found a Permission static field
Permission perm = (Permission) fieldValue;
String permissionName = field.getName();
if (Permission.class.isAssignableFrom(fieldValue.getClass())) {
// Found a Permission static field
Permission perm = (Permission) fieldValue;
String permissionName = field.getName();
registerPermission(perm, permissionName);
}
} catch (Exception ignore) {
}
}
}
registerPermission(perm, permissionName);
}
}
catch (Exception ignore) {
}
}
}
protected void registerPermission(Permission perm, String permissionName) {
Assert.notNull(perm, "Permission required");
Assert.hasText(permissionName, "Permission name required");
protected void registerPermission(Permission perm, String permissionName) {
Assert.notNull(perm, "Permission required");
Assert.hasText(permissionName, "Permission name required");
Integer mask = Integer.valueOf(perm.getMask());
Integer mask = Integer.valueOf(perm.getMask());
// Ensure no existing Permission uses this integer or code
Assert.isTrue(!registeredPermissionsByInteger.containsKey(mask), "An existing Permission already provides mask " + mask);
Assert.isTrue(!registeredPermissionsByName.containsKey(permissionName), "An existing Permission already provides name '" + permissionName + "'");
// Ensure no existing Permission uses this integer or code
Assert.isTrue(!registeredPermissionsByInteger.containsKey(mask),
"An existing Permission already provides mask " + mask);
Assert.isTrue(!registeredPermissionsByName.containsKey(permissionName),
"An existing Permission already provides name '" + permissionName + "'");
// Register the new Permission
registeredPermissionsByInteger.put(mask, perm);
registeredPermissionsByName.put(permissionName, perm);
}
// Register the new Permission
registeredPermissionsByInteger.put(mask, perm);
registeredPermissionsByName.put(permissionName, perm);
}
public Permission buildFromMask(int mask) {
if (registeredPermissionsByInteger.containsKey(Integer.valueOf(mask))) {
// The requested mask has an exact match against a statically-defined Permission, so return it
return registeredPermissionsByInteger.get(Integer.valueOf(mask));
}
public Permission buildFromMask(int mask) {
if (registeredPermissionsByInteger.containsKey(Integer.valueOf(mask))) {
// The requested mask has an exact match against a statically-defined
// Permission, so return it
return registeredPermissionsByInteger.get(Integer.valueOf(mask));
}
// To get this far, we have to use a CumulativePermission
CumulativePermission permission = new CumulativePermission();
// To get this far, we have to use a CumulativePermission
CumulativePermission permission = new CumulativePermission();
for (int i = 0; i < 32; i++) {
int permissionToCheck = 1 << i;
for (int i = 0; i < 32; i++) {
int permissionToCheck = 1 << i;
if ((mask & permissionToCheck) == permissionToCheck) {
Permission p = registeredPermissionsByInteger.get(Integer.valueOf(permissionToCheck));
if ((mask & permissionToCheck) == permissionToCheck) {
Permission p = registeredPermissionsByInteger.get(Integer
.valueOf(permissionToCheck));
if (p == null) {
throw new IllegalStateException("Mask '" + permissionToCheck + "' does not have a corresponding static Permission");
}
permission.set(p);
}
}
if (p == null) {
throw new IllegalStateException("Mask '" + permissionToCheck
+ "' does not have a corresponding static Permission");
}
permission.set(p);
}
}
return permission;
}
return permission;
}
public Permission buildFromName(String name) {
Permission p = registeredPermissionsByName.get(name);
public Permission buildFromName(String name) {
Permission p = registeredPermissionsByName.get(name);
if (p == null) {
throw new IllegalArgumentException("Unknown permission '" + name + "'");
}
if (p == null) {
throw new IllegalArgumentException("Unknown permission '" + name + "'");
}
return p;
}
return p;
}
public List<Permission> buildFromNames(List<String> names) {
if ((names == null) || (names.size() == 0)) {
return Collections.emptyList();
}
public List<Permission> buildFromNames(List<String> names) {
if ((names == null) || (names.size() == 0)) {
return Collections.emptyList();
}
List<Permission> permissions = new ArrayList<Permission>(names.size());
List<Permission> permissions = new ArrayList<Permission>(names.size());
for (String name : names) {
permissions.add(buildFromName(name));
}
for (String name : names) {
permissions.add(buildFromName(name));
}
return permissions;
}
return permissions;
}
}

View File

@ -12,108 +12,119 @@ import org.springframework.util.Assert;
public class DefaultPermissionGrantingStrategy implements PermissionGrantingStrategy {
private final transient AuditLogger auditLogger;
private final transient AuditLogger auditLogger;
/**
* Creates an instance with the logger which will be used to record granting and denial of requested permissions.
*/
public DefaultPermissionGrantingStrategy(AuditLogger auditLogger) {
Assert.notNull(auditLogger, "auditLogger cannot be null");
this.auditLogger = auditLogger;
}
/**
* Creates an instance with the logger which will be used to record granting and
* denial of requested permissions.
*/
public DefaultPermissionGrantingStrategy(AuditLogger auditLogger) {
Assert.notNull(auditLogger, "auditLogger cannot be null");
this.auditLogger = auditLogger;
}
/**
* Determines authorization. The order of the <code>permission</code> and <code>sid</code> arguments is
* <em>extremely important</em>! The method will iterate through each of the <code>permission</code>s in the order
* specified. For each iteration, all of the <code>sid</code>s will be considered, again in the order they are
* presented. A search will then be performed for the first {@link AccessControlEntry} object that directly
* matches that <code>permission:sid</code> combination. When the <em>first full match</em> is found (ie an ACE
* that has the SID currently being searched for and the exact permission bit mask being search for), the grant or
* deny flag for that ACE will prevail. If the ACE specifies to grant access, the method will return
* <code>true</code>. If the ACE specifies to deny access, the loop will stop and the next <code>permission</code>
* iteration will be performed. If each permission indicates to deny access, the first deny ACE found will be
* considered the reason for the failure (as it was the first match found, and is therefore the one most logically
* requiring changes - although not always). If absolutely no matching ACE was found at all for any permission,
* the parent ACL will be tried (provided that there is a parent and {@link Acl#isEntriesInheriting()} is
* <code>true</code>. The parent ACL will also scan its parent and so on. If ultimately no matching ACE is found,
* a <code>NotFoundException</code> will be thrown and the caller will need to decide how to handle the permission
* check. Similarly, if any of the SID arguments presented to the method were not loaded by the ACL,
* <code>UnloadedSidException</code> will be thrown.
*
* @param permission the exact permissions to scan for (order is important)
* @param sids the exact SIDs to scan for (order is important)
* @param administrativeMode if <code>true</code> denotes the query is for administrative purposes and no auditing
* will be undertaken
*
* @return <code>true</code> if one of the permissions has been granted, <code>false</code> if one of the
* permissions has been specifically revoked
*
* @throws NotFoundException if an exact ACE for one of the permission bit masks and SID combination could not be
* found
*/
public boolean isGranted(Acl acl, List<Permission> permission, List<Sid> sids, boolean administrativeMode)
throws NotFoundException {
/**
* Determines authorization. The order of the <code>permission</code> and
* <code>sid</code> arguments is <em>extremely important</em>! The method will iterate
* through each of the <code>permission</code>s in the order specified. For each
* iteration, all of the <code>sid</code>s will be considered, again in the order they
* are presented. A search will then be performed for the first
* {@link AccessControlEntry} object that directly matches that
* <code>permission:sid</code> combination. When the <em>first full match</em> is
* found (ie an ACE that has the SID currently being searched for and the exact
* permission bit mask being search for), the grant or deny flag for that ACE will
* prevail. If the ACE specifies to grant access, the method will return
* <code>true</code>. If the ACE specifies to deny access, the loop will stop and the
* next <code>permission</code> iteration will be performed. If each permission
* indicates to deny access, the first deny ACE found will be considered the reason
* for the failure (as it was the first match found, and is therefore the one most
* logically requiring changes - although not always). If absolutely no matching ACE
* was found at all for any permission, the parent ACL will be tried (provided that
* there is a parent and {@link Acl#isEntriesInheriting()} is <code>true</code>. The
* parent ACL will also scan its parent and so on. If ultimately no matching ACE is
* found, a <code>NotFoundException</code> will be thrown and the caller will need to
* decide how to handle the permission check. Similarly, if any of the SID arguments
* presented to the method were not loaded by the ACL,
* <code>UnloadedSidException</code> will be thrown.
*
* @param permission the exact permissions to scan for (order is important)
* @param sids the exact SIDs to scan for (order is important)
* @param administrativeMode if <code>true</code> denotes the query is for
* administrative purposes and no auditing will be undertaken
*
* @return <code>true</code> if one of the permissions has been granted,
* <code>false</code> if one of the permissions has been specifically revoked
*
* @throws NotFoundException if an exact ACE for one of the permission bit masks and
* SID combination could not be found
*/
public boolean isGranted(Acl acl, List<Permission> permission, List<Sid> sids,
boolean administrativeMode) throws NotFoundException {
final List<AccessControlEntry> aces = acl.getEntries();
final List<AccessControlEntry> aces = acl.getEntries();
AccessControlEntry firstRejection = null;
AccessControlEntry firstRejection = null;
for (Permission p : permission) {
for (Sid sid: sids) {
// Attempt to find exact match for this permission mask and SID
boolean scanNextSid = true;
for (Permission p : permission) {
for (Sid sid : sids) {
// Attempt to find exact match for this permission mask and SID
boolean scanNextSid = true;
for (AccessControlEntry ace : aces ) {
for (AccessControlEntry ace : aces) {
if ((ace.getPermission().getMask() == p.getMask()) && ace.getSid().equals(sid)) {
// Found a matching ACE, so its authorization decision will prevail
if (ace.isGranting()) {
// Success
if (!administrativeMode) {
auditLogger.logIfNeeded(true, ace);
}
if ((ace.getPermission().getMask() == p.getMask())
&& ace.getSid().equals(sid)) {
// Found a matching ACE, so its authorization decision will
// prevail
if (ace.isGranting()) {
// Success
if (!administrativeMode) {
auditLogger.logIfNeeded(true, ace);
}
return true;
}
return true;
}
// Failure for this permission, so stop search
// We will see if they have a different permission
// (this permission is 100% rejected for this SID)
if (firstRejection == null) {
// Store first rejection for auditing reasons
firstRejection = ace;
}
// Failure for this permission, so stop search
// We will see if they have a different permission
// (this permission is 100% rejected for this SID)
if (firstRejection == null) {
// Store first rejection for auditing reasons
firstRejection = ace;
}
scanNextSid = false; // helps break the loop
scanNextSid = false; // helps break the loop
break; // exit aces loop
}
}
break; // exit aces loop
}
}
if (!scanNextSid) {
break; // exit SID for loop (now try next permission)
}
}
}
if (!scanNextSid) {
break; // exit SID for loop (now try next permission)
}
}
}
if (firstRejection != null) {
// We found an ACE to reject the request at this point, as no
// other ACEs were found that granted a different permission
if (!administrativeMode) {
auditLogger.logIfNeeded(false, firstRejection);
}
if (firstRejection != null) {
// We found an ACE to reject the request at this point, as no
// other ACEs were found that granted a different permission
if (!administrativeMode) {
auditLogger.logIfNeeded(false, firstRejection);
}
return false;
}
return false;
}
// No matches have been found so far
if (acl.isEntriesInheriting() && (acl.getParentAcl() != null)) {
// We have a parent, so let them try to find a matching ACE
return acl.getParentAcl().isGranted(permission, sids, false);
} else {
// We either have no parent, or we're the uppermost parent
throw new NotFoundException("Unable to locate a matching ACE for passed permissions and SIDs");
}
}
// No matches have been found so far
if (acl.isEntriesInheriting() && (acl.getParentAcl() != null)) {
// We have a parent, so let them try to find a matching ACE
return acl.getParentAcl().isGranted(permission, sids, false);
}
else {
// We either have no parent, or we're the uppermost parent
throw new NotFoundException(
"Unable to locate a matching ACE for passed permissions and SIDs");
}
}
}

View File

@ -27,124 +27,135 @@ import org.springframework.security.acls.model.PermissionGrantingStrategy;
import org.springframework.security.util.FieldUtils;
import org.springframework.util.Assert;
/**
* Simple implementation of {@link AclCache} that delegates to EH-CACHE.
* <p>
* Designed to handle the transient fields in {@link AclImpl}. Note that this implementation assumes all
* {@link AclImpl} instances share the same {@link PermissionGrantingStrategy} and {@link AclAuthorizationStrategy}
* instances.
* Designed to handle the transient fields in {@link AclImpl}. Note that this
* implementation assumes all {@link AclImpl} instances share the same
* {@link PermissionGrantingStrategy} and {@link AclAuthorizationStrategy} instances.
*
* @author Ben Alex
*/
public class EhCacheBasedAclCache implements AclCache {
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private final Ehcache cache;
private PermissionGrantingStrategy permissionGrantingStrategy;
private AclAuthorizationStrategy aclAuthorizationStrategy;
private final Ehcache cache;
private PermissionGrantingStrategy permissionGrantingStrategy;
private AclAuthorizationStrategy aclAuthorizationStrategy;
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
public EhCacheBasedAclCache(Ehcache cache, PermissionGrantingStrategy permissionGrantingStrategy,
AclAuthorizationStrategy aclAuthorizationStrategy) {
Assert.notNull(cache, "Cache required");
Assert.notNull(permissionGrantingStrategy, "PermissionGrantingStrategy required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
this.cache = cache;
this.permissionGrantingStrategy = permissionGrantingStrategy;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
}
public EhCacheBasedAclCache(Ehcache cache,
PermissionGrantingStrategy permissionGrantingStrategy,
AclAuthorizationStrategy aclAuthorizationStrategy) {
Assert.notNull(cache, "Cache required");
Assert.notNull(permissionGrantingStrategy, "PermissionGrantingStrategy required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
this.cache = cache;
this.permissionGrantingStrategy = permissionGrantingStrategy;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public void evictFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
public void evictFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
MutableAcl acl = getFromCache(pk);
MutableAcl acl = getFromCache(pk);
if (acl != null) {
cache.remove(acl.getId());
cache.remove(acl.getObjectIdentity());
}
}
if (acl != null) {
cache.remove(acl.getId());
cache.remove(acl.getObjectIdentity());
}
}
public void evictFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
public void evictFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
MutableAcl acl = getFromCache(objectIdentity);
MutableAcl acl = getFromCache(objectIdentity);
if (acl != null) {
cache.remove(acl.getId());
cache.remove(acl.getObjectIdentity());
}
}
if (acl != null) {
cache.remove(acl.getId());
cache.remove(acl.getObjectIdentity());
}
}
public MutableAcl getFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
public MutableAcl getFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
Element element = null;
Element element = null;
try {
element = cache.get(objectIdentity);
} catch (CacheException ignored) {}
try {
element = cache.get(objectIdentity);
}
catch (CacheException ignored) {
}
if (element == null) {
return null;
}
if (element == null) {
return null;
}
return initializeTransientFields((MutableAcl)element.getValue());
}
return initializeTransientFields((MutableAcl) element.getValue());
}
public MutableAcl getFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
public MutableAcl getFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
Element element = null;
Element element = null;
try {
element = cache.get(pk);
} catch (CacheException ignored) {}
try {
element = cache.get(pk);
}
catch (CacheException ignored) {
}
if (element == null) {
return null;
}
if (element == null) {
return null;
}
return initializeTransientFields((MutableAcl) element.getValue());
}
return initializeTransientFields((MutableAcl) element.getValue());
}
public void putInCache(MutableAcl acl) {
Assert.notNull(acl, "Acl required");
Assert.notNull(acl.getObjectIdentity(), "ObjectIdentity required");
Assert.notNull(acl.getId(), "ID required");
public void putInCache(MutableAcl acl) {
Assert.notNull(acl, "Acl required");
Assert.notNull(acl.getObjectIdentity(), "ObjectIdentity required");
Assert.notNull(acl.getId(), "ID required");
if (this.aclAuthorizationStrategy == null) {
if (acl instanceof AclImpl) {
this.aclAuthorizationStrategy = (AclAuthorizationStrategy) FieldUtils.getProtectedFieldValue("aclAuthorizationStrategy", acl);
this.permissionGrantingStrategy = (PermissionGrantingStrategy) FieldUtils.getProtectedFieldValue("permissionGrantingStrategy", acl);
}
}
if (this.aclAuthorizationStrategy == null) {
if (acl instanceof AclImpl) {
this.aclAuthorizationStrategy = (AclAuthorizationStrategy) FieldUtils
.getProtectedFieldValue("aclAuthorizationStrategy", acl);
this.permissionGrantingStrategy = (PermissionGrantingStrategy) FieldUtils
.getProtectedFieldValue("permissionGrantingStrategy", acl);
}
}
if ((acl.getParentAcl() != null) && (acl.getParentAcl() instanceof MutableAcl)) {
putInCache((MutableAcl) acl.getParentAcl());
}
if ((acl.getParentAcl() != null) && (acl.getParentAcl() instanceof MutableAcl)) {
putInCache((MutableAcl) acl.getParentAcl());
}
cache.put(new Element(acl.getObjectIdentity(), acl));
cache.put(new Element(acl.getId(), acl));
}
cache.put(new Element(acl.getObjectIdentity(), acl));
cache.put(new Element(acl.getId(), acl));
}
private MutableAcl initializeTransientFields(MutableAcl value) {
if (value instanceof AclImpl) {
FieldUtils.setProtectedFieldValue("aclAuthorizationStrategy", value, this.aclAuthorizationStrategy);
FieldUtils.setProtectedFieldValue("permissionGrantingStrategy", value, this.permissionGrantingStrategy);
}
private MutableAcl initializeTransientFields(MutableAcl value) {
if (value instanceof AclImpl) {
FieldUtils.setProtectedFieldValue("aclAuthorizationStrategy", value,
this.aclAuthorizationStrategy);
FieldUtils.setProtectedFieldValue("permissionGrantingStrategy", value,
this.permissionGrantingStrategy);
}
if (value.getParentAcl() != null) {
initializeTransientFields((MutableAcl) value.getParentAcl());
}
return value;
}
if (value.getParentAcl() != null) {
initializeTransientFields((MutableAcl) value.getParentAcl());
}
return value;
}
public void clearCache() {
cache.removeAll();
}
public void clearCache() {
cache.removeAll();
}
}

View File

@ -19,53 +19,61 @@ import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.Assert;
/**
* Represents a <code>GrantedAuthority</code> as a <code>Sid</code>.<p>This is a basic implementation that simply
* uses the <code>String</code>-based principal for <code>Sid</code> comparison. More complex principal objects may
* wish to provide an alternative <code>Sid</code> implementation that uses some other identifier.</p>
* Represents a <code>GrantedAuthority</code> as a <code>Sid</code>.
* <p>
* This is a basic implementation that simply uses the <code>String</code>-based principal
* for <code>Sid</code> comparison. More complex principal objects may wish to provide an
* alternative <code>Sid</code> implementation that uses some other identifier.
* </p>
*
* @author Ben Alex
*/
public class GrantedAuthoritySid implements Sid {
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private final String grantedAuthority;
private final String grantedAuthority;
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
public GrantedAuthoritySid(String grantedAuthority) {
Assert.hasText(grantedAuthority, "GrantedAuthority required");
this.grantedAuthority = grantedAuthority;
}
public GrantedAuthoritySid(String grantedAuthority) {
Assert.hasText(grantedAuthority, "GrantedAuthority required");
this.grantedAuthority = grantedAuthority;
}
public GrantedAuthoritySid(GrantedAuthority grantedAuthority) {
Assert.notNull(grantedAuthority, "GrantedAuthority required");
Assert.notNull(grantedAuthority.getAuthority(),
"This Sid is only compatible with GrantedAuthoritys that provide a non-null getAuthority()");
this.grantedAuthority = grantedAuthority.getAuthority();
}
public GrantedAuthoritySid(GrantedAuthority grantedAuthority) {
Assert.notNull(grantedAuthority, "GrantedAuthority required");
Assert.notNull(
grantedAuthority.getAuthority(),
"This Sid is only compatible with GrantedAuthoritys that provide a non-null getAuthority()");
this.grantedAuthority = grantedAuthority.getAuthority();
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public boolean equals(Object object) {
if ((object == null) || !(object instanceof GrantedAuthoritySid)) {
return false;
}
public boolean equals(Object object) {
if ((object == null) || !(object instanceof GrantedAuthoritySid)) {
return false;
}
// Delegate to getGrantedAuthority() to perform actual comparison (both should be identical)
return ((GrantedAuthoritySid) object).getGrantedAuthority().equals(this.getGrantedAuthority());
}
// Delegate to getGrantedAuthority() to perform actual comparison (both should be
// identical)
return ((GrantedAuthoritySid) object).getGrantedAuthority().equals(
this.getGrantedAuthority());
}
public int hashCode() {
return this.getGrantedAuthority().hashCode();
}
public int hashCode() {
return this.getGrantedAuthority().hashCode();
}
public String getGrantedAuthority() {
return grantedAuthority;
}
public String getGrantedAuthority() {
return grantedAuthority;
}
public String toString() {
return "GrantedAuthoritySid[" + this.grantedAuthority + "]";
}
public String toString() {
return "GrantedAuthoritySid[" + this.grantedAuthority + "]";
}
}

View File

@ -20,25 +20,26 @@ package org.springframework.security.acls.domain;
* @author Ben Alex
*/
public class IdentityUnavailableException extends RuntimeException {
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
/**
* Constructs an <code>IdentityUnavailableException</code> with the specified message.
*
* @param msg the detail message
*/
public IdentityUnavailableException(String msg) {
super(msg);
}
/**
* Constructs an <code>IdentityUnavailableException</code> with the specified message.
*
* @param msg the detail message
*/
public IdentityUnavailableException(String msg) {
super(msg);
}
/**
* Constructs an <code>IdentityUnavailableException</code> with the specified message
* and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public IdentityUnavailableException(String msg, Throwable t) {
super(msg, t);
}
/**
* Constructs an <code>IdentityUnavailableException</code> with the specified message
* and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public IdentityUnavailableException(String msg, Throwable t) {
super(msg, t);
}
}

View File

@ -21,137 +21,145 @@ import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Simple implementation of {@link ObjectIdentity}.
* <p>
* Uses <code>String</code>s to store the identity of the domain object instance. Also offers a constructor that uses
* reflection to build the identity information.
* Uses <code>String</code>s to store the identity of the domain object instance. Also
* offers a constructor that uses reflection to build the identity information.
*
* @author Ben Alex
*/
public class ObjectIdentityImpl implements ObjectIdentity {
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private final String type;
private Serializable identifier;
private final String type;
private Serializable identifier;
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
public ObjectIdentityImpl(String type, Serializable identifier) {
Assert.hasText(type, "Type required");
Assert.notNull(identifier, "identifier required");
public ObjectIdentityImpl(String type, Serializable identifier) {
Assert.hasText(type, "Type required");
Assert.notNull(identifier, "identifier required");
this.identifier = identifier;
this.type = type;
}
this.identifier = identifier;
this.type = type;
}
/**
* Constructor which uses the name of the supplied class as the <tt>type</tt> property.
*/
public ObjectIdentityImpl(Class<?> javaType, Serializable identifier) {
Assert.notNull(javaType, "Java Type required");
Assert.notNull(identifier, "identifier required");
this.type = javaType.getName();
this.identifier = identifier;
}
/**
* Constructor which uses the name of the supplied class as the <tt>type</tt>
* property.
*/
public ObjectIdentityImpl(Class<?> javaType, Serializable identifier) {
Assert.notNull(javaType, "Java Type required");
Assert.notNull(identifier, "identifier required");
this.type = javaType.getName();
this.identifier = identifier;
}
/**
* Creates the <code>ObjectIdentityImpl</code> based on the passed
* object instance. The passed object must provide a <code>getId()</code>
* method, otherwise an exception will be thrown.
* <p>
* The class name of the object passed will be considered the {@link #type}, so if more control is required,
* a different constructor should be used.
*
* @param object the domain object instance to create an identity for.
*
* @throws IdentityUnavailableException if identity could not be extracted
*/
public ObjectIdentityImpl(Object object) throws IdentityUnavailableException {
Assert.notNull(object, "object cannot be null");
/**
* Creates the <code>ObjectIdentityImpl</code> based on the passed object instance.
* The passed object must provide a <code>getId()</code> method, otherwise an
* exception will be thrown.
* <p>
* The class name of the object passed will be considered the {@link #type}, so if
* more control is required, a different constructor should be used.
*
* @param object the domain object instance to create an identity for.
*
* @throws IdentityUnavailableException if identity could not be extracted
*/
public ObjectIdentityImpl(Object object) throws IdentityUnavailableException {
Assert.notNull(object, "object cannot be null");
Class<?> typeClass = ClassUtils.getUserClass(object.getClass());
type = typeClass.getName();
Class<?> typeClass = ClassUtils.getUserClass(object.getClass());
type = typeClass.getName();
Object result;
Object result;
try {
Method method = typeClass.getMethod("getId", new Class[] {});
result = method.invoke(object);
} catch (Exception e) {
throw new IdentityUnavailableException("Could not extract identity from object " + object, e);
}
try {
Method method = typeClass.getMethod("getId", new Class[] {});
result = method.invoke(object);
}
catch (Exception e) {
throw new IdentityUnavailableException(
"Could not extract identity from object " + object, e);
}
Assert.notNull(result, "getId() is required to return a non-null value");
Assert.isInstanceOf(Serializable.class, result, "Getter must provide a return value of type Serializable");
this.identifier = (Serializable) result;
}
Assert.notNull(result, "getId() is required to return a non-null value");
Assert.isInstanceOf(Serializable.class, result,
"Getter must provide a return value of type Serializable");
this.identifier = (Serializable) result;
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
/**
* Important so caching operates properly.
* <p>
* Considers an object of the same class equal if it has the same <code>classname</code> and
* <code>id</code> properties.
* <p>
* Numeric identities (Integer and Long values) are considered equal if they are numerically equal. Other
* serializable types are evaluated using a simple equality.
*
* @param arg0 object to compare
*
* @return <code>true</code> if the presented object matches this object
*/
public boolean equals(Object arg0) {
if (arg0 == null || !(arg0 instanceof ObjectIdentityImpl)) {
return false;
}
/**
* Important so caching operates properly.
* <p>
* Considers an object of the same class equal if it has the same
* <code>classname</code> and <code>id</code> properties.
* <p>
* Numeric identities (Integer and Long values) are considered equal if they are
* numerically equal. Other serializable types are evaluated using a simple equality.
*
* @param arg0 object to compare
*
* @return <code>true</code> if the presented object matches this object
*/
public boolean equals(Object arg0) {
if (arg0 == null || !(arg0 instanceof ObjectIdentityImpl)) {
return false;
}
ObjectIdentityImpl other = (ObjectIdentityImpl) arg0;
ObjectIdentityImpl other = (ObjectIdentityImpl) arg0;
if (identifier instanceof Number && other.identifier instanceof Number) {
// Integers and Longs with same value should be considered equal
if (((Number)identifier).longValue() != ((Number)other.identifier).longValue()) {
return false;
}
} else {
// Use plain equality for other serializable types
if (!identifier.equals(other.identifier)) {
return false;
}
}
if (identifier instanceof Number && other.identifier instanceof Number) {
// Integers and Longs with same value should be considered equal
if (((Number) identifier).longValue() != ((Number) other.identifier)
.longValue()) {
return false;
}
}
else {
// Use plain equality for other serializable types
if (!identifier.equals(other.identifier)) {
return false;
}
}
return type.equals(other.type);
}
return type.equals(other.type);
}
public Serializable getIdentifier() {
return identifier;
}
public Serializable getIdentifier() {
return identifier;
}
public String getType() {
return type;
}
public String getType() {
return type;
}
/**
* Important so caching operates properly.
*
* @return the hash
*/
public int hashCode() {
int code = 31;
code ^= this.type.hashCode();
code ^= this.identifier.hashCode();
/**
* Important so caching operates properly.
*
* @return the hash
*/
public int hashCode() {
int code = 31;
code ^= this.type.hashCode();
code ^= this.identifier.hashCode();
return code;
}
return code;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(this.getClass().getName()).append("[");
sb.append("Type: ").append(this.type);
sb.append("; Identifier: ").append(this.identifier).append("]");
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(this.getClass().getName()).append("[");
sb.append("Type: ").append(this.type);
sb.append("; Identifier: ").append(this.identifier).append("]");
return sb.toString();
}
return sb.toString();
}
}

View File

@ -22,19 +22,22 @@ import org.springframework.security.acls.model.ObjectIdentityGenerator;
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
/**
* Basic implementation of {@link ObjectIdentityRetrievalStrategy} and <tt>ObjectIdentityGenerator</tt>
* that uses the constructors of {@link ObjectIdentityImpl} to create the {@link ObjectIdentity}.
* Basic implementation of {@link ObjectIdentityRetrievalStrategy} and
* <tt>ObjectIdentityGenerator</tt> that uses the constructors of
* {@link ObjectIdentityImpl} to create the {@link ObjectIdentity}.
*
* @author Ben Alex
*/
public class ObjectIdentityRetrievalStrategyImpl implements ObjectIdentityRetrievalStrategy, ObjectIdentityGenerator {
//~ Methods ========================================================================================================
public class ObjectIdentityRetrievalStrategyImpl implements
ObjectIdentityRetrievalStrategy, ObjectIdentityGenerator {
// ~ Methods
// ========================================================================================================
public ObjectIdentity getObjectIdentity(Object domainObject) {
return new ObjectIdentityImpl(domainObject);
}
public ObjectIdentity getObjectIdentity(Object domainObject) {
return new ObjectIdentityImpl(domainObject);
}
public ObjectIdentity createObjectIdentity(Serializable id, String type) {
return new ObjectIdentityImpl(type, id);
}
public ObjectIdentity createObjectIdentity(Serializable id, String type) {
return new ObjectIdentityImpl(type, id);
}
}

View File

@ -5,7 +5,8 @@ import java.util.List;
import org.springframework.security.acls.model.Permission;
/**
* Provides a simple mechanism to retrieve {@link Permission} instances from integer masks.
* Provides a simple mechanism to retrieve {@link Permission} instances from integer
* masks.
*
* @author Ben Alex
* @since 2.0.3
@ -13,19 +14,17 @@ import org.springframework.security.acls.model.Permission;
*/
public interface PermissionFactory {
/**
* Dynamically creates a <code>CumulativePermission</code> or <code>BasePermission</code> representing the
* active bits in the passed mask.
*
* @param mask to build
*
* @return a Permission representing the requested object
*/
Permission buildFromMask(int mask);
/**
* Dynamically creates a <code>CumulativePermission</code> or
* <code>BasePermission</code> representing the active bits in the passed mask.
*
* @param mask to build
*
* @return a Permission representing the requested object
*/
Permission buildFromMask(int mask);
Permission buildFromName(String name);
Permission buildFromName(String name);
List<Permission> buildFromNames(List<String> names);
List<Permission> buildFromNames(List<String> names);
}

View File

@ -14,64 +14,70 @@
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;
/**
* Represents an <code>Authentication.getPrincipal()</code> as a <code>Sid</code>.<p>This is a basic implementation
* that simply uses the <code>String</code>-based principal for <code>Sid</code> comparison. More complex principal
* objects may wish to provide an alternative <code>Sid</code> implementation that uses some other identifier.</p>
* Represents an <code>Authentication.getPrincipal()</code> as a <code>Sid</code>.
* <p>
* This is a basic implementation that simply uses the <code>String</code>-based principal
* for <code>Sid</code> comparison. More complex principal objects may wish to provide an
* alternative <code>Sid</code> implementation that uses some other identifier.
* </p>
*
* @author Ben Alex
*/
public class PrincipalSid implements Sid {
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private final String principal;
private final String principal;
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
public PrincipalSid(String principal) {
Assert.hasText(principal, "Principal required");
this.principal = principal;
}
public PrincipalSid(String principal) {
Assert.hasText(principal, "Principal required");
this.principal = principal;
}
public PrincipalSid(Authentication authentication) {
Assert.notNull(authentication, "Authentication required");
Assert.notNull(authentication.getPrincipal(), "Principal required");
public PrincipalSid(Authentication authentication) {
Assert.notNull(authentication, "Authentication required");
Assert.notNull(authentication.getPrincipal(), "Principal required");
if (authentication.getPrincipal() instanceof UserDetails) {
this.principal = ((UserDetails) authentication.getPrincipal()).getUsername();
} else {
this.principal = authentication.getPrincipal().toString();
}
}
if (authentication.getPrincipal() instanceof UserDetails) {
this.principal = ((UserDetails) authentication.getPrincipal()).getUsername();
}
else {
this.principal = authentication.getPrincipal().toString();
}
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public boolean equals(Object object) {
if ((object == null) || !(object instanceof PrincipalSid)) {
return false;
}
public boolean equals(Object object) {
if ((object == null) || !(object instanceof PrincipalSid)) {
return false;
}
// Delegate to getPrincipal() to perform actual comparison (both should be identical)
return ((PrincipalSid) object).getPrincipal().equals(this.getPrincipal());
}
// Delegate to getPrincipal() to perform actual comparison (both should be
// identical)
return ((PrincipalSid) object).getPrincipal().equals(this.getPrincipal());
}
public int hashCode() {
return this.getPrincipal().hashCode();
}
public int hashCode() {
return this.getPrincipal().hashCode();
}
public String getPrincipal() {
return principal;
}
public String getPrincipal() {
return principal;
}
public String toString() {
return "PrincipalSid[" + this.principal + "]";
}
public String toString() {
return "PrincipalSid[" + this.principal + "]";
}
}

View File

@ -28,38 +28,42 @@ import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.Assert;
/**
* Basic implementation of {@link SidRetrievalStrategy} that creates a {@link Sid} for the principal, as well as
* every granted authority the principal holds. Can optionally have a <tt>RoleHierarchy</tt> injected in order to
* determine the extended list of authorities that the principal is assigned.
* Basic implementation of {@link SidRetrievalStrategy} that creates a {@link Sid} for the
* principal, as well as every granted authority the principal holds. Can optionally have
* a <tt>RoleHierarchy</tt> injected in order to determine the extended list of
* authorities that the principal is assigned.
* <p>
* The returned array will always contain the {@link PrincipalSid} before any {@link GrantedAuthoritySid} elements.
* The returned array will always contain the {@link PrincipalSid} before any
* {@link GrantedAuthoritySid} elements.
*
* @author Ben Alex
*/
public class SidRetrievalStrategyImpl implements SidRetrievalStrategy {
private RoleHierarchy roleHierarchy = new NullRoleHierarchy();
private RoleHierarchy roleHierarchy = new NullRoleHierarchy();
public SidRetrievalStrategyImpl() {
}
public SidRetrievalStrategyImpl() {
}
public SidRetrievalStrategyImpl(RoleHierarchy roleHierarchy) {
Assert.notNull(roleHierarchy, "RoleHierarchy must not be null");
this.roleHierarchy = roleHierarchy;
}
public SidRetrievalStrategyImpl(RoleHierarchy roleHierarchy) {
Assert.notNull(roleHierarchy, "RoleHierarchy must not be null");
this.roleHierarchy = roleHierarchy;
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public List<Sid> getSids(Authentication authentication) {
Collection<? extends GrantedAuthority> authorities = roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities());
List<Sid> sids = new ArrayList<Sid>(authorities.size() + 1);
public List<Sid> getSids(Authentication authentication) {
Collection<? extends GrantedAuthority> authorities = roleHierarchy
.getReachableGrantedAuthorities(authentication.getAuthorities());
List<Sid> sids = new ArrayList<Sid>(authorities.size() + 1);
sids.add(new PrincipalSid(authentication));
sids.add(new PrincipalSid(authentication));
for (GrantedAuthority authority : authorities) {
sids.add(new GrantedAuthoritySid(authority));
}
for (GrantedAuthority authority : authorities) {
sids.add(new GrantedAuthoritySid(authority));
}
return sids;
}
return sids;
}
}

View File

@ -25,106 +25,114 @@ import org.springframework.util.Assert;
import java.io.Serializable;
/**
* Simple implementation of {@link org.springframework.security.acls.model.AclCache} that delegates to {@link Cache} implementation.
* Simple implementation of {@link org.springframework.security.acls.model.AclCache} that
* delegates to {@link Cache} implementation.
* <p>
* Designed to handle the transient fields in {@link org.springframework.security.acls.domain.AclImpl}. Note that this implementation assumes all
* {@link org.springframework.security.acls.domain.AclImpl} instances share the same {@link org.springframework.security.acls.model.PermissionGrantingStrategy} and {@link org.springframework.security.acls.domain.AclAuthorizationStrategy}
* instances.
* Designed to handle the transient fields in
* {@link org.springframework.security.acls.domain.AclImpl}. Note that this implementation
* assumes all {@link org.springframework.security.acls.domain.AclImpl} instances share
* the same {@link org.springframework.security.acls.model.PermissionGrantingStrategy} and
* {@link org.springframework.security.acls.domain.AclAuthorizationStrategy} instances.
*
* @author Marten Deinum
* @since 3.2
*/
public class SpringCacheBasedAclCache implements AclCache {
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private final Cache cache;
private PermissionGrantingStrategy permissionGrantingStrategy;
private AclAuthorizationStrategy aclAuthorizationStrategy;
private final Cache cache;
private PermissionGrantingStrategy permissionGrantingStrategy;
private AclAuthorizationStrategy aclAuthorizationStrategy;
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
public SpringCacheBasedAclCache(Cache cache, PermissionGrantingStrategy permissionGrantingStrategy,
AclAuthorizationStrategy aclAuthorizationStrategy) {
Assert.notNull(cache, "Cache required");
Assert.notNull(permissionGrantingStrategy, "PermissionGrantingStrategy required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
this.cache = cache;
this.permissionGrantingStrategy = permissionGrantingStrategy;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
}
public SpringCacheBasedAclCache(Cache cache,
PermissionGrantingStrategy permissionGrantingStrategy,
AclAuthorizationStrategy aclAuthorizationStrategy) {
Assert.notNull(cache, "Cache required");
Assert.notNull(permissionGrantingStrategy, "PermissionGrantingStrategy required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
this.cache = cache;
this.permissionGrantingStrategy = permissionGrantingStrategy;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public void evictFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
public void evictFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
MutableAcl acl = getFromCache(pk);
MutableAcl acl = getFromCache(pk);
if (acl != null) {
cache.evict(acl.getId());
cache.evict(acl.getObjectIdentity());
}
}
if (acl != null) {
cache.evict(acl.getId());
cache.evict(acl.getObjectIdentity());
}
}
public void evictFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
public void evictFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
MutableAcl acl = getFromCache(objectIdentity);
MutableAcl acl = getFromCache(objectIdentity);
if (acl != null) {
cache.evict(acl.getId());
cache.evict(acl.getObjectIdentity());
}
}
if (acl != null) {
cache.evict(acl.getId());
cache.evict(acl.getObjectIdentity());
}
}
public MutableAcl getFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
return getFromCache((Object)objectIdentity);
}
public MutableAcl getFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
return getFromCache((Object) objectIdentity);
}
public MutableAcl getFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
return getFromCache((Object)pk);
}
public MutableAcl getFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
return getFromCache((Object) pk);
}
public void putInCache(MutableAcl acl) {
Assert.notNull(acl, "Acl required");
Assert.notNull(acl.getObjectIdentity(), "ObjectIdentity required");
Assert.notNull(acl.getId(), "ID required");
public void putInCache(MutableAcl acl) {
Assert.notNull(acl, "Acl required");
Assert.notNull(acl.getObjectIdentity(), "ObjectIdentity required");
Assert.notNull(acl.getId(), "ID required");
if ((acl.getParentAcl() != null) && (acl.getParentAcl() instanceof MutableAcl)) {
putInCache((MutableAcl) acl.getParentAcl());
}
if ((acl.getParentAcl() != null) && (acl.getParentAcl() instanceof MutableAcl)) {
putInCache((MutableAcl) acl.getParentAcl());
}
cache.put(acl.getObjectIdentity(), acl);
cache.put(acl.getId(), acl);
}
cache.put(acl.getObjectIdentity(), acl);
cache.put(acl.getId(), acl);
}
private MutableAcl getFromCache(Object key) {
Cache.ValueWrapper element = cache.get(key);
private MutableAcl getFromCache(Object key) {
Cache.ValueWrapper element = cache.get(key);
if (element == null) {
return null;
}
if (element == null) {
return null;
}
return initializeTransientFields((MutableAcl) element.get());
}
return initializeTransientFields((MutableAcl) element.get());
}
private MutableAcl initializeTransientFields(MutableAcl value) {
if (value instanceof AclImpl) {
FieldUtils.setProtectedFieldValue("aclAuthorizationStrategy", value, this.aclAuthorizationStrategy);
FieldUtils.setProtectedFieldValue("permissionGrantingStrategy", value, this.permissionGrantingStrategy);
}
private MutableAcl initializeTransientFields(MutableAcl value) {
if (value instanceof AclImpl) {
FieldUtils.setProtectedFieldValue("aclAuthorizationStrategy", value,
this.aclAuthorizationStrategy);
FieldUtils.setProtectedFieldValue("permissionGrantingStrategy", value,
this.permissionGrantingStrategy);
}
if (value.getParentAcl() != null) {
initializeTransientFields((MutableAcl) value.getParentAcl());
}
return value;
}
if (value.getParentAcl() != null) {
initializeTransientFields((MutableAcl) value.getParentAcl());
}
return value;
}
public void clearCache() {
cache.clear();
}
public void clearCache() {
cache.clear();
}
}

View File

@ -34,96 +34,107 @@ import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.Sid;
import org.springframework.util.Assert;
/**
* Simple JDBC-based implementation of <code>AclService</code>.
* <p>
* Requires the "dirty" flags in {@link org.springframework.security.acls.domain.AclImpl} and
* {@link org.springframework.security.acls.domain.AccessControlEntryImpl} to be set, so that the implementation can
* detect changed parameters easily.
* Requires the "dirty" flags in {@link org.springframework.security.acls.domain.AclImpl}
* and {@link org.springframework.security.acls.domain.AccessControlEntryImpl} to be set,
* so that the implementation can detect changed parameters easily.
*
* @author Ben Alex
*/
public class JdbcAclService implements AclService {
//~ Static fields/initializers =====================================================================================
// ~ Static fields/initializers
// =====================================================================================
protected static final Log log = LogFactory.getLog(JdbcAclService.class);
private static final String DEFAULT_SELECT_ACL_WITH_PARENT_SQL = "select obj.object_id_identity as obj_id, class.class as class "
+ "from acl_object_identity obj, acl_object_identity parent, acl_class class "
+ "where obj.parent_object = parent.id and obj.object_id_class = class.id "
+ "and parent.object_id_identity = ? and parent.object_id_class = ("
+ "select id FROM acl_class where acl_class.class = ?)";
protected static final Log log = LogFactory.getLog(JdbcAclService.class);
private static final String DEFAULT_SELECT_ACL_WITH_PARENT_SQL = "select obj.object_id_identity as obj_id, class.class as class "
+ "from acl_object_identity obj, acl_object_identity parent, acl_class class "
+ "where obj.parent_object = parent.id and obj.object_id_class = class.id "
+ "and parent.object_id_identity = ? and parent.object_id_class = ("
+ "select id FROM acl_class where acl_class.class = ?)";
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
protected final JdbcTemplate jdbcTemplate;
private final LookupStrategy lookupStrategy;
private String findChildrenSql = DEFAULT_SELECT_ACL_WITH_PARENT_SQL;
protected final JdbcTemplate jdbcTemplate;
private final LookupStrategy lookupStrategy;
private String findChildrenSql = DEFAULT_SELECT_ACL_WITH_PARENT_SQL;
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
public JdbcAclService(DataSource dataSource, LookupStrategy lookupStrategy) {
Assert.notNull(dataSource, "DataSource required");
Assert.notNull(lookupStrategy, "LookupStrategy required");
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.lookupStrategy = lookupStrategy;
}
public JdbcAclService(DataSource dataSource, LookupStrategy lookupStrategy) {
Assert.notNull(dataSource, "DataSource required");
Assert.notNull(lookupStrategy, "LookupStrategy required");
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.lookupStrategy = lookupStrategy;
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
Object[] args = {parentIdentity.getIdentifier(), parentIdentity.getType()};
List<ObjectIdentity> objects = jdbcTemplate.query(findChildrenSql, args,
new RowMapper<ObjectIdentity>() {
public ObjectIdentity mapRow(ResultSet rs, int rowNum) throws SQLException {
String javaType = rs.getString("class");
Long identifier = new Long(rs.getLong("obj_id"));
public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
Object[] args = { parentIdentity.getIdentifier(), parentIdentity.getType() };
List<ObjectIdentity> objects = jdbcTemplate.query(findChildrenSql, args,
new RowMapper<ObjectIdentity>() {
public ObjectIdentity mapRow(ResultSet rs, int rowNum)
throws SQLException {
String javaType = rs.getString("class");
Long identifier = new Long(rs.getLong("obj_id"));
return new ObjectIdentityImpl(javaType, identifier);
}
});
return new ObjectIdentityImpl(javaType, identifier);
}
});
if (objects.size() == 0) {
return null;
}
if (objects.size() == 0) {
return null;
}
return objects;
}
return objects;
}
public Acl readAclById(ObjectIdentity object, List<Sid> sids) throws NotFoundException {
Map<ObjectIdentity, Acl> map = readAclsById(Arrays.asList(object), sids);
Assert.isTrue(map.containsKey(object), "There should have been an Acl entry for ObjectIdentity " + object);
public Acl readAclById(ObjectIdentity object, List<Sid> sids)
throws NotFoundException {
Map<ObjectIdentity, Acl> map = readAclsById(Arrays.asList(object), sids);
Assert.isTrue(map.containsKey(object),
"There should have been an Acl entry for ObjectIdentity " + object);
return (Acl) map.get(object);
}
return (Acl) map.get(object);
}
public Acl readAclById(ObjectIdentity object) throws NotFoundException {
return readAclById(object, null);
}
public Acl readAclById(ObjectIdentity object) throws NotFoundException {
return readAclById(object, null);
}
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects) throws NotFoundException {
return readAclsById(objects, null);
}
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects)
throws NotFoundException {
return readAclsById(objects, null);
}
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids) throws NotFoundException {
Map<ObjectIdentity, Acl> result = lookupStrategy.readAclsById(objects, sids);
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects,
List<Sid> sids) throws NotFoundException {
Map<ObjectIdentity, Acl> result = lookupStrategy.readAclsById(objects, sids);
// Check every requested object identity was found (throw NotFoundException if needed)
for (ObjectIdentity oid : objects) {
if (!result.containsKey(oid)) {
throw new NotFoundException("Unable to find ACL information for object identity '" + oid + "'");
}
}
// Check every requested object identity was found (throw NotFoundException if
// needed)
for (ObjectIdentity oid : objects) {
if (!result.containsKey(oid)) {
throw new NotFoundException(
"Unable to find ACL information for object identity '" + oid
+ "'");
}
}
return result;
}
return result;
}
/**
* Allows customization of the SQL query used to find child object identities.
*
* @param findChildrenSql
*/
public void setFindChildrenQuery(String findChildrenSql) {
this.findChildrenSql = findChildrenSql;
}
/**
* Allows customization of the SQL query used to find child object identities.
*
* @param findChildrenSql
*/
public void setFindChildrenQuery(String findChildrenSql) {
this.findChildrenSql = findChildrenSql;
}
}

View File

@ -41,409 +41,447 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
/**
* Provides a base JDBC implementation of {@link MutableAclService}.
* <p>
* The default settings are for HSQLDB. If you are using a different database you
* will probably need to set the {@link #setSidIdentityQuery(String) sidIdentityQuery} and
* {@link #setClassIdentityQuery(String) classIdentityQuery} properties appropriately. The other queries,
* SQL inserts and updates can also be customized to accomodate schema variations, but must produce results
* consistent with those expected by the defaults.
* The default settings are for HSQLDB. If you are using a different database you will
* probably need to set the {@link #setSidIdentityQuery(String) sidIdentityQuery} and
* {@link #setClassIdentityQuery(String) classIdentityQuery} properties appropriately. The
* other queries, SQL inserts and updates can also be customized to accomodate schema
* variations, but must produce results consistent with those expected by the defaults.
* <p>
* See the appendix of the Spring Security reference manual for more information on the expected schema
* and how it is used. Information on using PostgreSQL is also included.
* See the appendix of the Spring Security reference manual for more information on the
* expected schema and how it is used. Information on using PostgreSQL is also included.
*
* @author Ben Alex
* @author Johannes Zlattinger
*/
public class JdbcMutableAclService extends JdbcAclService implements MutableAclService {
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private boolean foreignKeysInDatabase = true;
private final AclCache aclCache;
private String deleteEntryByObjectIdentityForeignKey = "delete from acl_entry where acl_object_identity=?";
private String deleteObjectIdentityByPrimaryKey = "delete from acl_object_identity where id=?";
private String classIdentityQuery = "call identity()";
private String sidIdentityQuery = "call identity()";
private String insertClass = "insert into acl_class (class) values (?)";
private String insertEntry = "insert into acl_entry "
+ "(acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure)"
+ "values (?, ?, ?, ?, ?, ?, ?)";
private String insertObjectIdentity = "insert into acl_object_identity "
+ "(object_id_class, object_id_identity, owner_sid, entries_inheriting) " + "values (?, ?, ?, ?)";
private String insertSid = "insert into acl_sid (principal, sid) values (?, ?)";
private String selectClassPrimaryKey = "select id from acl_class where class=?";
private String selectObjectIdentityPrimaryKey = "select acl_object_identity.id from acl_object_identity, acl_class "
+ "where acl_object_identity.object_id_class = acl_class.id and acl_class.class=? "
+ "and acl_object_identity.object_id_identity = ?";
private String selectSidPrimaryKey = "select id from acl_sid where principal=? and sid=?";
private String updateObjectIdentity = "update acl_object_identity set "
+ "parent_object = ?, owner_sid = ?, entries_inheriting = ?" + " where id = ?";
private boolean foreignKeysInDatabase = true;
private final AclCache aclCache;
private String deleteEntryByObjectIdentityForeignKey = "delete from acl_entry where acl_object_identity=?";
private String deleteObjectIdentityByPrimaryKey = "delete from acl_object_identity where id=?";
private String classIdentityQuery = "call identity()";
private String sidIdentityQuery = "call identity()";
private String insertClass = "insert into acl_class (class) values (?)";
private String insertEntry = "insert into acl_entry "
+ "(acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure)"
+ "values (?, ?, ?, ?, ?, ?, ?)";
private String insertObjectIdentity = "insert into acl_object_identity "
+ "(object_id_class, object_id_identity, owner_sid, entries_inheriting) "
+ "values (?, ?, ?, ?)";
private String insertSid = "insert into acl_sid (principal, sid) values (?, ?)";
private String selectClassPrimaryKey = "select id from acl_class where class=?";
private String selectObjectIdentityPrimaryKey = "select acl_object_identity.id from acl_object_identity, acl_class "
+ "where acl_object_identity.object_id_class = acl_class.id and acl_class.class=? "
+ "and acl_object_identity.object_id_identity = ?";
private String selectSidPrimaryKey = "select id from acl_sid where principal=? and sid=?";
private String updateObjectIdentity = "update acl_object_identity set "
+ "parent_object = ?, owner_sid = ?, entries_inheriting = ?"
+ " where id = ?";
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
public JdbcMutableAclService(DataSource dataSource, LookupStrategy lookupStrategy, AclCache aclCache) {
super(dataSource, lookupStrategy);
Assert.notNull(aclCache, "AclCache required");
this.aclCache = aclCache;
}
public JdbcMutableAclService(DataSource dataSource, LookupStrategy lookupStrategy,
AclCache aclCache) {
super(dataSource, lookupStrategy);
Assert.notNull(aclCache, "AclCache required");
this.aclCache = aclCache;
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public MutableAcl createAcl(ObjectIdentity objectIdentity) throws AlreadyExistsException {
Assert.notNull(objectIdentity, "Object Identity required");
public MutableAcl createAcl(ObjectIdentity objectIdentity)
throws AlreadyExistsException {
Assert.notNull(objectIdentity, "Object Identity required");
// Check this object identity hasn't already been persisted
if (retrieveObjectIdentityPrimaryKey(objectIdentity) != null) {
throw new AlreadyExistsException("Object identity '" + objectIdentity + "' already exists");
}
// Check this object identity hasn't already been persisted
if (retrieveObjectIdentityPrimaryKey(objectIdentity) != null) {
throw new AlreadyExistsException("Object identity '" + objectIdentity
+ "' already exists");
}
// Need to retrieve the current principal, in order to know who "owns" this ACL (can be changed later on)
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
PrincipalSid sid = new PrincipalSid(auth);
// Need to retrieve the current principal, in order to know who "owns" this ACL
// (can be changed later on)
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
PrincipalSid sid = new PrincipalSid(auth);
// Create the acl_object_identity row
createObjectIdentity(objectIdentity, sid);
// Create the acl_object_identity row
createObjectIdentity(objectIdentity, sid);
// Retrieve the ACL via superclass (ensures cache registration, proper retrieval etc)
Acl acl = readAclById(objectIdentity);
Assert.isInstanceOf(MutableAcl.class, acl, "MutableAcl should be been returned");
// Retrieve the ACL via superclass (ensures cache registration, proper retrieval
// etc)
Acl acl = readAclById(objectIdentity);
Assert.isInstanceOf(MutableAcl.class, acl, "MutableAcl should be been returned");
return (MutableAcl) acl;
}
return (MutableAcl) acl;
}
/**
* Creates a new row in acl_entry for every ACE defined in the passed MutableAcl object.
*
* @param acl containing the ACEs to insert
*/
protected void createEntries(final MutableAcl acl) {
if(acl.getEntries().isEmpty()) {
return;
}
jdbcTemplate.batchUpdate(insertEntry,
new BatchPreparedStatementSetter() {
public int getBatchSize() {
return acl.getEntries().size();
}
/**
* Creates a new row in acl_entry for every ACE defined in the passed MutableAcl
* object.
*
* @param acl containing the ACEs to insert
*/
protected void createEntries(final MutableAcl acl) {
if (acl.getEntries().isEmpty()) {
return;
}
jdbcTemplate.batchUpdate(insertEntry, new BatchPreparedStatementSetter() {
public int getBatchSize() {
return acl.getEntries().size();
}
public void setValues(PreparedStatement stmt, int i) throws SQLException {
AccessControlEntry entry_ = acl.getEntries().get(i);
Assert.isTrue(entry_ instanceof AccessControlEntryImpl, "Unknown ACE class");
AccessControlEntryImpl entry = (AccessControlEntryImpl) entry_;
public void setValues(PreparedStatement stmt, int i) throws SQLException {
AccessControlEntry entry_ = acl.getEntries().get(i);
Assert.isTrue(entry_ instanceof AccessControlEntryImpl,
"Unknown ACE class");
AccessControlEntryImpl entry = (AccessControlEntryImpl) entry_;
stmt.setLong(1, ((Long) acl.getId()).longValue());
stmt.setInt(2, i);
stmt.setLong(3, createOrRetrieveSidPrimaryKey(entry.getSid(), true).longValue());
stmt.setInt(4, entry.getPermission().getMask());
stmt.setBoolean(5, entry.isGranting());
stmt.setBoolean(6, entry.isAuditSuccess());
stmt.setBoolean(7, entry.isAuditFailure());
}
});
}
stmt.setLong(1, ((Long) acl.getId()).longValue());
stmt.setInt(2, i);
stmt.setLong(3, createOrRetrieveSidPrimaryKey(entry.getSid(), true)
.longValue());
stmt.setInt(4, entry.getPermission().getMask());
stmt.setBoolean(5, entry.isGranting());
stmt.setBoolean(6, entry.isAuditSuccess());
stmt.setBoolean(7, entry.isAuditFailure());
}
});
}
/**
* Creates an entry in the acl_object_identity table for the passed ObjectIdentity. The Sid is also
* necessary, as acl_object_identity has defined the sid column as non-null.
*
* @param object to represent an acl_object_identity for
* @param owner for the SID column (will be created if there is no acl_sid entry for this particular Sid already)
*/
protected void createObjectIdentity(ObjectIdentity object, Sid owner) {
Long sidId = createOrRetrieveSidPrimaryKey(owner, true);
Long classId = createOrRetrieveClassPrimaryKey(object.getType(), true);
jdbcTemplate.update(insertObjectIdentity, classId, object.getIdentifier(), sidId, Boolean.TRUE);
}
/**
* Creates an entry in the acl_object_identity table for the passed ObjectIdentity.
* The Sid is also necessary, as acl_object_identity has defined the sid column as
* non-null.
*
* @param object to represent an acl_object_identity for
* @param owner for the SID column (will be created if there is no acl_sid entry for
* this particular Sid already)
*/
protected void createObjectIdentity(ObjectIdentity object, Sid owner) {
Long sidId = createOrRetrieveSidPrimaryKey(owner, true);
Long classId = createOrRetrieveClassPrimaryKey(object.getType(), true);
jdbcTemplate.update(insertObjectIdentity, classId, object.getIdentifier(), sidId,
Boolean.TRUE);
}
/**
* Retrieves the primary key from {@code acl_class}, creating a new row if needed and the
* {@code allowCreate} property is {@code true}.
*
* @param type to find or create an entry for (often the fully-qualified class name)
* @param allowCreate true if creation is permitted if not found
*
* @return the primary key or null if not found
*/
protected Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate) {
List<Long> classIds = jdbcTemplate.queryForList(selectClassPrimaryKey, new Object[] {type}, Long.class);
/**
* Retrieves the primary key from {@code acl_class}, creating a new row if needed and
* the {@code allowCreate} property is {@code true}.
*
* @param type to find or create an entry for (often the fully-qualified class name)
* @param allowCreate true if creation is permitted if not found
*
* @return the primary key or null if not found
*/
protected Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate) {
List<Long> classIds = jdbcTemplate.queryForList(selectClassPrimaryKey,
new Object[] { type }, Long.class);
if (!classIds.isEmpty()) {
return classIds.get(0);
}
if (!classIds.isEmpty()) {
return classIds.get(0);
}
if (allowCreate) {
jdbcTemplate.update(insertClass, type);
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(),
"Transaction must be running");
return new Long(jdbcTemplate.queryForLong(classIdentityQuery));
}
if (allowCreate) {
jdbcTemplate.update(insertClass, type);
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(),
"Transaction must be running");
return new Long(jdbcTemplate.queryForLong(classIdentityQuery));
}
return null;
}
return null;
}
/**
* Retrieves the primary key from acl_sid, creating a new row if needed and the allowCreate property is
* true.
*
* @param sid to find or create
* @param allowCreate true if creation is permitted if not found
*
* @return the primary key or null if not found
*
* @throws IllegalArgumentException if the <tt>Sid</tt> is not a recognized implementation.
*/
protected Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) {
Assert.notNull(sid, "Sid required");
/**
* Retrieves the primary key from acl_sid, creating a new row if needed and the
* allowCreate property is true.
*
* @param sid to find or create
* @param allowCreate true if creation is permitted if not found
*
* @return the primary key or null if not found
*
* @throws IllegalArgumentException if the <tt>Sid</tt> is not a recognized
* implementation.
*/
protected Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) {
Assert.notNull(sid, "Sid required");
String sidName;
boolean sidIsPrincipal = true;
String sidName;
boolean sidIsPrincipal = true;
if (sid instanceof PrincipalSid) {
sidName = ((PrincipalSid) sid).getPrincipal();
} else if (sid instanceof GrantedAuthoritySid) {
sidName = ((GrantedAuthoritySid) sid).getGrantedAuthority();
sidIsPrincipal = false;
} else {
throw new IllegalArgumentException("Unsupported implementation of Sid");
}
if (sid instanceof PrincipalSid) {
sidName = ((PrincipalSid) sid).getPrincipal();
}
else if (sid instanceof GrantedAuthoritySid) {
sidName = ((GrantedAuthoritySid) sid).getGrantedAuthority();
sidIsPrincipal = false;
}
else {
throw new IllegalArgumentException("Unsupported implementation of Sid");
}
return createOrRetrieveSidPrimaryKey(sidName, sidIsPrincipal, allowCreate);
}
return createOrRetrieveSidPrimaryKey(sidName, sidIsPrincipal, allowCreate);
}
/**
* Retrieves the primary key from acl_sid, creating a new row if needed and the allowCreate property is
* true.
* @param sidName name of Sid to find or to create
* @param sidIsPrincipal whether it's a user or granted authority like role
* @param allowCreate true if creation is permitted if not found
* @return the primary key or null if not found
*/
protected Long createOrRetrieveSidPrimaryKey(String sidName, boolean sidIsPrincipal, boolean allowCreate) {
/**
* Retrieves the primary key from acl_sid, creating a new row if needed and the
* allowCreate property is true.
* @param sidName name of Sid to find or to create
* @param sidIsPrincipal whether it's a user or granted authority like role
* @param allowCreate true if creation is permitted if not found
* @return the primary key or null if not found
*/
protected Long createOrRetrieveSidPrimaryKey(String sidName, boolean sidIsPrincipal,
boolean allowCreate) {
List<Long> sidIds = jdbcTemplate.queryForList(selectSidPrimaryKey,
new Object[] {Boolean.valueOf(sidIsPrincipal), sidName}, Long.class);
List<Long> sidIds = jdbcTemplate.queryForList(selectSidPrimaryKey, new Object[] {
Boolean.valueOf(sidIsPrincipal), sidName }, Long.class);
if (!sidIds.isEmpty()) {
return sidIds.get(0);
}
if (!sidIds.isEmpty()) {
return sidIds.get(0);
}
if (allowCreate) {
jdbcTemplate.update(insertSid, Boolean.valueOf(sidIsPrincipal), sidName);
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(), "Transaction must be running");
return new Long(jdbcTemplate.queryForLong(sidIdentityQuery));
}
if (allowCreate) {
jdbcTemplate.update(insertSid, Boolean.valueOf(sidIsPrincipal), sidName);
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(),
"Transaction must be running");
return new Long(jdbcTemplate.queryForLong(sidIdentityQuery));
}
return null;
}
return null;
}
public void deleteAcl(ObjectIdentity objectIdentity, boolean deleteChildren) throws ChildrenExistException {
Assert.notNull(objectIdentity, "Object Identity required");
Assert.notNull(objectIdentity.getIdentifier(), "Object Identity doesn't provide an identifier");
public void deleteAcl(ObjectIdentity objectIdentity, boolean deleteChildren)
throws ChildrenExistException {
Assert.notNull(objectIdentity, "Object Identity required");
Assert.notNull(objectIdentity.getIdentifier(),
"Object Identity doesn't provide an identifier");
if (deleteChildren) {
List<ObjectIdentity> children = findChildren(objectIdentity);
if (children != null) {
for (ObjectIdentity child : children) {
deleteAcl(child, true);
}
}
} else {
if (!foreignKeysInDatabase) {
// We need to perform a manual verification for what a FK would normally do
// We generally don't do this, in the interests of deadlock management
List<ObjectIdentity> children = findChildren(objectIdentity);
if (children != null) {
throw new ChildrenExistException("Cannot delete '" + objectIdentity + "' (has " + children.size()
+ " children)");
}
}
}
if (deleteChildren) {
List<ObjectIdentity> children = findChildren(objectIdentity);
if (children != null) {
for (ObjectIdentity child : children) {
deleteAcl(child, true);
}
}
}
else {
if (!foreignKeysInDatabase) {
// We need to perform a manual verification for what a FK would normally
// do
// We generally don't do this, in the interests of deadlock management
List<ObjectIdentity> children = findChildren(objectIdentity);
if (children != null) {
throw new ChildrenExistException("Cannot delete '" + objectIdentity
+ "' (has " + children.size() + " children)");
}
}
}
Long oidPrimaryKey = retrieveObjectIdentityPrimaryKey(objectIdentity);
Long oidPrimaryKey = retrieveObjectIdentityPrimaryKey(objectIdentity);
// Delete this ACL's ACEs in the acl_entry table
deleteEntries(oidPrimaryKey);
// Delete this ACL's ACEs in the acl_entry table
deleteEntries(oidPrimaryKey);
// Delete this ACL's acl_object_identity row
deleteObjectIdentity(oidPrimaryKey);
// Delete this ACL's acl_object_identity row
deleteObjectIdentity(oidPrimaryKey);
// Clear the cache
aclCache.evictFromCache(objectIdentity);
}
// Clear the cache
aclCache.evictFromCache(objectIdentity);
}
/**
* Deletes all ACEs defined in the acl_entry table belonging to the presented ObjectIdentity primary key.
*
* @param oidPrimaryKey the rows in acl_entry to delete
*/
protected void deleteEntries(Long oidPrimaryKey) {
jdbcTemplate.update(deleteEntryByObjectIdentityForeignKey, oidPrimaryKey);
}
/**
* Deletes all ACEs defined in the acl_entry table belonging to the presented
* ObjectIdentity primary key.
*
* @param oidPrimaryKey the rows in acl_entry to delete
*/
protected void deleteEntries(Long oidPrimaryKey) {
jdbcTemplate.update(deleteEntryByObjectIdentityForeignKey, oidPrimaryKey);
}
/**
* Deletes a single row from acl_object_identity that is associated with the presented ObjectIdentity primary key.
* <p>
* We do not delete any entries from acl_class, even if no classes are using that class any longer. This is a
* deadlock avoidance approach.
*
* @param oidPrimaryKey to delete the acl_object_identity
*/
protected void deleteObjectIdentity(Long oidPrimaryKey) {
// Delete the acl_object_identity row
jdbcTemplate.update(deleteObjectIdentityByPrimaryKey, oidPrimaryKey);
}
/**
* Deletes a single row from acl_object_identity that is associated with the presented
* ObjectIdentity primary key.
* <p>
* We do not delete any entries from acl_class, even if no classes are using that
* class any longer. This is a deadlock avoidance approach.
*
* @param oidPrimaryKey to delete the acl_object_identity
*/
protected void deleteObjectIdentity(Long oidPrimaryKey) {
// Delete the acl_object_identity row
jdbcTemplate.update(deleteObjectIdentityByPrimaryKey, oidPrimaryKey);
}
/**
* Retrieves the primary key from the acl_object_identity table for the passed ObjectIdentity. Unlike some
* other methods in this implementation, this method will NOT create a row (use {@link
* #createObjectIdentity(ObjectIdentity, Sid)} instead).
*
* @param oid to find
*
* @return the object identity or null if not found
*/
protected Long retrieveObjectIdentityPrimaryKey(ObjectIdentity oid) {
try {
return new Long(jdbcTemplate.queryForLong(selectObjectIdentityPrimaryKey, oid.getType(), oid.getIdentifier()));
} catch (DataAccessException notFound) {
return null;
}
}
/**
* Retrieves the primary key from the acl_object_identity table for the passed
* ObjectIdentity. Unlike some other methods in this implementation, this method will
* NOT create a row (use {@link #createObjectIdentity(ObjectIdentity, Sid)} instead).
*
* @param oid to find
*
* @return the object identity or null if not found
*/
protected Long retrieveObjectIdentityPrimaryKey(ObjectIdentity oid) {
try {
return new Long(jdbcTemplate.queryForLong(selectObjectIdentityPrimaryKey,
oid.getType(), oid.getIdentifier()));
}
catch (DataAccessException notFound) {
return null;
}
}
/**
* This implementation will simply delete all ACEs in the database and recreate them on each invocation of
* this method. A more comprehensive implementation might use dirty state checking, or more likely use ORM
* capabilities for create, update and delete operations of {@link MutableAcl}.
*/
public MutableAcl updateAcl(MutableAcl acl) throws NotFoundException {
Assert.notNull(acl.getId(), "Object Identity doesn't provide an identifier");
/**
* This implementation will simply delete all ACEs in the database and recreate them
* on each invocation of this method. A more comprehensive implementation might use
* dirty state checking, or more likely use ORM capabilities for create, update and
* delete operations of {@link MutableAcl}.
*/
public MutableAcl updateAcl(MutableAcl acl) throws NotFoundException {
Assert.notNull(acl.getId(), "Object Identity doesn't provide an identifier");
// Delete this ACL's ACEs in the acl_entry table
deleteEntries(retrieveObjectIdentityPrimaryKey(acl.getObjectIdentity()));
// Delete this ACL's ACEs in the acl_entry table
deleteEntries(retrieveObjectIdentityPrimaryKey(acl.getObjectIdentity()));
// Create this ACL's ACEs in the acl_entry table
createEntries(acl);
// Create this ACL's ACEs in the acl_entry table
createEntries(acl);
// Change the mutable columns in acl_object_identity
updateObjectIdentity(acl);
// Change the mutable columns in acl_object_identity
updateObjectIdentity(acl);
// Clear the cache, including children
clearCacheIncludingChildren(acl.getObjectIdentity());
// Clear the cache, including children
clearCacheIncludingChildren(acl.getObjectIdentity());
// Retrieve the ACL via superclass (ensures cache registration, proper retrieval etc)
return (MutableAcl) super.readAclById(acl.getObjectIdentity());
}
// Retrieve the ACL via superclass (ensures cache registration, proper retrieval
// etc)
return (MutableAcl) super.readAclById(acl.getObjectIdentity());
}
private void clearCacheIncludingChildren(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
List<ObjectIdentity> children = findChildren(objectIdentity);
if (children != null) {
for (ObjectIdentity child : children) {
clearCacheIncludingChildren(child);
}
}
aclCache.evictFromCache(objectIdentity);
}
private void clearCacheIncludingChildren(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
List<ObjectIdentity> children = findChildren(objectIdentity);
if (children != null) {
for (ObjectIdentity child : children) {
clearCacheIncludingChildren(child);
}
}
aclCache.evictFromCache(objectIdentity);
}
/**
* Updates an existing acl_object_identity row, with new information presented in the passed MutableAcl
* object. Also will create an acl_sid entry if needed for the Sid that owns the MutableAcl.
*
* @param acl to modify (a row must already exist in acl_object_identity)
*
* @throws NotFoundException if the ACL could not be found to update.
*/
protected void updateObjectIdentity(MutableAcl acl) {
Long parentId = null;
/**
* Updates an existing acl_object_identity row, with new information presented in the
* passed MutableAcl object. Also will create an acl_sid entry if needed for the Sid
* that owns the MutableAcl.
*
* @param acl to modify (a row must already exist in acl_object_identity)
*
* @throws NotFoundException if the ACL could not be found to update.
*/
protected void updateObjectIdentity(MutableAcl acl) {
Long parentId = null;
if (acl.getParentAcl() != null) {
Assert.isInstanceOf(ObjectIdentityImpl.class, acl.getParentAcl().getObjectIdentity(),
"Implementation only supports ObjectIdentityImpl");
if (acl.getParentAcl() != null) {
Assert.isInstanceOf(ObjectIdentityImpl.class, acl.getParentAcl()
.getObjectIdentity(),
"Implementation only supports ObjectIdentityImpl");
ObjectIdentityImpl oii = (ObjectIdentityImpl) acl.getParentAcl().getObjectIdentity();
parentId = retrieveObjectIdentityPrimaryKey(oii);
}
ObjectIdentityImpl oii = (ObjectIdentityImpl) acl.getParentAcl()
.getObjectIdentity();
parentId = retrieveObjectIdentityPrimaryKey(oii);
}
Assert.notNull(acl.getOwner(), "Owner is required in this implementation");
Assert.notNull(acl.getOwner(), "Owner is required in this implementation");
Long ownerSid = createOrRetrieveSidPrimaryKey(acl.getOwner(), true);
int count = jdbcTemplate.update(updateObjectIdentity,
parentId, ownerSid, Boolean.valueOf(acl.isEntriesInheriting()), acl.getId());
Long ownerSid = createOrRetrieveSidPrimaryKey(acl.getOwner(), true);
int count = jdbcTemplate.update(updateObjectIdentity, parentId, ownerSid,
Boolean.valueOf(acl.isEntriesInheriting()), acl.getId());
if (count != 1) {
throw new NotFoundException("Unable to locate ACL to update");
}
}
if (count != 1) {
throw new NotFoundException("Unable to locate ACL to update");
}
}
/**
* Sets the query that will be used to retrieve the identity of a newly created row in the <tt>acl_class</tt>
* table.
*
* @param classIdentityQuery the query, which should return the identifier. Defaults to <tt>call identity()</tt>
*/
public void setClassIdentityQuery(String classIdentityQuery) {
Assert.hasText(classIdentityQuery, "New classIdentityQuery query is required");
this.classIdentityQuery = classIdentityQuery;
}
/**
* Sets the query that will be used to retrieve the identity of a newly created row in
* the <tt>acl_class</tt> table.
*
* @param classIdentityQuery the query, which should return the identifier. Defaults
* to <tt>call identity()</tt>
*/
public void setClassIdentityQuery(String classIdentityQuery) {
Assert.hasText(classIdentityQuery, "New classIdentityQuery query is required");
this.classIdentityQuery = classIdentityQuery;
}
/**
* Sets the query that will be used to retrieve the identity of a newly created row in the <tt>acl_sid</tt>
* table.
*
* @param sidIdentityQuery the query, which should return the identifier. Defaults to <tt>call identity()</tt>
*/
public void setSidIdentityQuery(String sidIdentityQuery) {
Assert.hasText(sidIdentityQuery, "New sidIdentityQuery query is required");
this.sidIdentityQuery = sidIdentityQuery;
}
/**
* Sets the query that will be used to retrieve the identity of a newly created row in
* the <tt>acl_sid</tt> table.
*
* @param sidIdentityQuery the query, which should return the identifier. Defaults to
* <tt>call identity()</tt>
*/
public void setSidIdentityQuery(String sidIdentityQuery) {
Assert.hasText(sidIdentityQuery, "New sidIdentityQuery query is required");
this.sidIdentityQuery = sidIdentityQuery;
}
public void setDeleteEntryByObjectIdentityForeignKeySql(String deleteEntryByObjectIdentityForeignKey) {
this.deleteEntryByObjectIdentityForeignKey = deleteEntryByObjectIdentityForeignKey;
}
public void setDeleteEntryByObjectIdentityForeignKeySql(
String deleteEntryByObjectIdentityForeignKey) {
this.deleteEntryByObjectIdentityForeignKey = deleteEntryByObjectIdentityForeignKey;
}
public void setDeleteObjectIdentityByPrimaryKeySql(String deleteObjectIdentityByPrimaryKey) {
this.deleteObjectIdentityByPrimaryKey = deleteObjectIdentityByPrimaryKey;
}
public void setDeleteObjectIdentityByPrimaryKeySql(
String deleteObjectIdentityByPrimaryKey) {
this.deleteObjectIdentityByPrimaryKey = deleteObjectIdentityByPrimaryKey;
}
public void setInsertClassSql(String insertClass) {
this.insertClass = insertClass;
}
public void setInsertClassSql(String insertClass) {
this.insertClass = insertClass;
}
public void setInsertEntrySql(String insertEntry) {
this.insertEntry = insertEntry;
}
public void setInsertEntrySql(String insertEntry) {
this.insertEntry = insertEntry;
}
public void setInsertObjectIdentitySql(String insertObjectIdentity) {
this.insertObjectIdentity = insertObjectIdentity;
}
public void setInsertObjectIdentitySql(String insertObjectIdentity) {
this.insertObjectIdentity = insertObjectIdentity;
}
public void setInsertSidSql(String insertSid) {
this.insertSid = insertSid;
}
public void setInsertSidSql(String insertSid) {
this.insertSid = insertSid;
}
public void setClassPrimaryKeyQuery(String selectClassPrimaryKey) {
this.selectClassPrimaryKey = selectClassPrimaryKey;
}
public void setClassPrimaryKeyQuery(String selectClassPrimaryKey) {
this.selectClassPrimaryKey = selectClassPrimaryKey;
}
public void setObjectIdentityPrimaryKeyQuery(String selectObjectIdentityPrimaryKey) {
this.selectObjectIdentityPrimaryKey = selectObjectIdentityPrimaryKey;
}
public void setObjectIdentityPrimaryKeyQuery(String selectObjectIdentityPrimaryKey) {
this.selectObjectIdentityPrimaryKey = selectObjectIdentityPrimaryKey;
}
public void setSidPrimaryKeyQuery(String selectSidPrimaryKey) {
this.selectSidPrimaryKey = selectSidPrimaryKey;
}
public void setSidPrimaryKeyQuery(String selectSidPrimaryKey) {
this.selectSidPrimaryKey = selectSidPrimaryKey;
}
public void setUpdateObjectIdentity(String updateObjectIdentity) {
this.updateObjectIdentity = updateObjectIdentity;
}
public void setUpdateObjectIdentity(String updateObjectIdentity) {
this.updateObjectIdentity = updateObjectIdentity;
}
/**
* @param foreignKeysInDatabase if false this class will perform additional FK constrain checking, which may
* cause deadlocks (the default is true, so deadlocks are avoided but the database is expected to enforce FKs)
*/
public void setForeignKeysInDatabase(boolean foreignKeysInDatabase) {
this.foreignKeysInDatabase = foreignKeysInDatabase;
}
/**
* @param foreignKeysInDatabase if false this class will perform additional FK
* constrain checking, which may cause deadlocks (the default is true, so deadlocks
* are avoided but the database is expected to enforce FKs)
*/
public void setForeignKeysInDatabase(boolean foreignKeysInDatabase) {
this.foreignKeysInDatabase = foreignKeysInDatabase;
}
}

View File

@ -22,26 +22,27 @@ import org.springframework.security.acls.model.Sid;
import java.util.List;
import java.util.Map;
/**
* Performs lookups for {@link org.springframework.security.acls.model.AclService}.
*
* @author Ben Alex
*/
public interface LookupStrategy {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
/**
* Perform database-specific optimized lookup.
*
* @param objects the identities to lookup (required)
* @param sids the SIDs for which identities are required (may be <tt>null</tt> - implementations may elect not
* to provide SID optimisations)
*
* @return a <tt>Map</tt> where keys represent the {@link ObjectIdentity} of the located {@link Acl} and values
* are the located {@link Acl} (never <tt>null</tt> although some entries may be missing; this method
* should not throw {@link NotFoundException}, as a chain of {@link LookupStrategy}s may be used
* to automatically create entries if required)
*/
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids);
/**
* Perform database-specific optimized lookup.
*
* @param objects the identities to lookup (required)
* @param sids the SIDs for which identities are required (may be <tt>null</tt> -
* implementations may elect not to provide SID optimisations)
*
* @return a <tt>Map</tt> where keys represent the {@link ObjectIdentity} of the
* located {@link Acl} and values are the located {@link Acl} (never <tt>null</tt>
* although some entries may be missing; this method should not throw
* {@link NotFoundException}, as a chain of {@link LookupStrategy}s may be used to
* automatically create entries if required)
*/
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids);
}

View File

@ -14,42 +14,41 @@
*/
package org.springframework.security.acls.model;
import java.io.Serializable;
/**
* Represents an individual permission assignment within an {@link Acl}.
*
* <p>
* Instances MUST be immutable, as they are returned by <code>Acl</code>
* and should not allow client modification.
* Instances MUST be immutable, as they are returned by <code>Acl</code> and should not
* allow client modification.
* </p>
*
* @author Ben Alex
*
*/
public interface AccessControlEntry extends Serializable {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
Acl getAcl();
Acl getAcl();
/**
* Obtains an identifier that represents this ACE.
*
* @return the identifier, or <code>null</code> if unsaved
*/
Serializable getId();
/**
* Obtains an identifier that represents this ACE.
*
* @return the identifier, or <code>null</code> if unsaved
*/
Serializable getId();
Permission getPermission();
Permission getPermission();
Sid getSid();
Sid getSid();
/**
* Indicates the a Permission is being granted to the relevant Sid. If false, indicates the permission is
* being revoked/blocked.
*
* @return true if being granted, false otherwise
*/
boolean isGranting();
/**
* Indicates the a Permission is being granted to the relevant Sid. If false,
* indicates the permission is being revoked/blocked.
*
* @return true if being granted, false otherwise
*/
boolean isGranting();
}

View File

@ -14,155 +14,188 @@
*/
package org.springframework.security.acls.model;
import java.io.Serializable;
import java.util.List;
/**
* Represents an access control list (ACL) for a domain object.
*
* <p>
* An <tt>Acl</tt> represents all ACL entries for a given domain object. In
* order to avoid needing references to the domain object itself, this
* interface handles indirection between a domain object and an ACL object
* identity via the {@link
* org.springframework.security.acls.model.ObjectIdentity} interface.
* An <tt>Acl</tt> represents all ACL entries for a given domain object. In order to avoid
* needing references to the domain object itself, this interface handles indirection
* between a domain object and an ACL object identity via the
* {@link org.springframework.security.acls.model.ObjectIdentity} interface.
* </p>
*
* <p>
* Implementing classes may elect to return instances that represent
* {@link org.springframework.security.acls.model.Permission} information for either
* some OR all {@link org.springframework.security.acls.model.Sid}
* instances. Therefore, an instance may NOT necessarily contain ALL <tt>Sid</tt>s
* for a given domain object.
* {@link org.springframework.security.acls.model.Permission} information for either some
* OR all {@link org.springframework.security.acls.model.Sid} instances. Therefore, an
* instance may NOT necessarily contain ALL <tt>Sid</tt>s for a given domain object.
* </p>
*
* @author Ben Alex
*/
public interface Acl extends Serializable {
/**
* Returns all of the entries represented by the present <tt>Acl</tt>. Entries associated with
* the <tt>Acl</tt> parents are not returned.
*
* <p>This method is typically used for administrative purposes.</p>
*
* <p>The order that entries appear in the array is important for methods declared in the
* {@link MutableAcl} interface. Furthermore, some implementations MAY use ordering as
* part of advanced permission checking.</p>
*
* <p>Do <em>NOT</em> use this method for making authorization decisions. Instead use {@link
* #isGranted(List, List, boolean)}.</p>
*
* <p>This method must operate correctly even if the <tt>Acl</tt> only represents a subset of
* <tt>Sid</tt>s. The caller is responsible for correctly handling the result if only a subset of
* <tt>Sid</tt>s is represented.</p>
*
* @return the list of entries represented by the <tt>Acl</tt>, or <tt>null</tt> if there are
* no entries presently associated with this <tt>Acl</tt>.
*/
List<AccessControlEntry> getEntries();
/**
* Returns all of the entries represented by the present <tt>Acl</tt>. Entries
* associated with the <tt>Acl</tt> parents are not returned.
*
* <p>
* This method is typically used for administrative purposes.
* </p>
*
* <p>
* The order that entries appear in the array is important for methods declared in the
* {@link MutableAcl} interface. Furthermore, some implementations MAY use ordering as
* part of advanced permission checking.
* </p>
*
* <p>
* Do <em>NOT</em> use this method for making authorization decisions. Instead use
* {@link #isGranted(List, List, boolean)}.
* </p>
*
* <p>
* This method must operate correctly even if the <tt>Acl</tt> only represents a
* subset of <tt>Sid</tt>s. The caller is responsible for correctly handling the
* result if only a subset of <tt>Sid</tt>s is represented.
* </p>
*
* @return the list of entries represented by the <tt>Acl</tt>, or <tt>null</tt> if
* there are no entries presently associated with this <tt>Acl</tt>.
*/
List<AccessControlEntry> getEntries();
/**
* Obtains the domain object this <tt>Acl</tt> provides entries for. This is immutable once an
* <tt>Acl</tt> is created.
*
* @return the object identity (never <tt>null</tt>)
*/
ObjectIdentity getObjectIdentity();
/**
* Obtains the domain object this <tt>Acl</tt> provides entries for. This is immutable
* once an <tt>Acl</tt> is created.
*
* @return the object identity (never <tt>null</tt>)
*/
ObjectIdentity getObjectIdentity();
/**
* Determines the owner of the <tt>Acl</tt>. The meaning of ownership varies by implementation and is
* unspecified.
*
* @return the owner (may be <tt>null</tt> if the implementation does not use ownership concepts)
*/
Sid getOwner();
/**
* Determines the owner of the <tt>Acl</tt>. The meaning of ownership varies by
* implementation and is unspecified.
*
* @return the owner (may be <tt>null</tt> if the implementation does not use
* ownership concepts)
*/
Sid getOwner();
/**
* A domain object may have a parent for the purpose of ACL inheritance. If there is a parent, its ACL can
* be accessed via this method. In turn, the parent's parent (grandparent) can be accessed and so on.
*
* <p>This method solely represents the presence of a navigation hierarchy between the parent <tt>Acl</tt> and this
* <tt>Acl</tt>. For actual inheritance to take place, the {@link #isEntriesInheriting()} must also be
* <tt>true</tt>.</p>
*
* <p>This method must operate correctly even if the <tt>Acl</tt> only represents a subset of
* <tt>Sid</tt>s. The caller is responsible for correctly handling the result if only a subset of
* <tt>Sid</tt>s is represented.</p>
*
* @return the parent <tt>Acl</tt> (may be <tt>null</tt> if this <tt>Acl</tt> does not have a parent)
*/
Acl getParentAcl();
/**
* A domain object may have a parent for the purpose of ACL inheritance. If there is a
* parent, its ACL can be accessed via this method. In turn, the parent's parent
* (grandparent) can be accessed and so on.
*
* <p>
* This method solely represents the presence of a navigation hierarchy between the
* parent <tt>Acl</tt> and this <tt>Acl</tt>. For actual inheritance to take place,
* the {@link #isEntriesInheriting()} must also be <tt>true</tt>.
* </p>
*
* <p>
* This method must operate correctly even if the <tt>Acl</tt> only represents a
* subset of <tt>Sid</tt>s. The caller is responsible for correctly handling the
* result if only a subset of <tt>Sid</tt>s is represented.
* </p>
*
* @return the parent <tt>Acl</tt> (may be <tt>null</tt> if this <tt>Acl</tt> does not
* have a parent)
*/
Acl getParentAcl();
/**
* Indicates whether the ACL entries from the {@link #getParentAcl()} should flow down into the current
* <tt>Acl</tt>.<p>The mere link between an <tt>Acl</tt> and a parent <tt>Acl</tt> on its own
* is insufficient to cause ACL entries to inherit down. This is because a domain object may wish to have entirely
* independent entries, but maintain the link with the parent for navigation purposes. Thus, this method denotes
* whether or not the navigation relationship also extends to the actual inheritance of entries.</p>
*
* @return <tt>true</tt> if parent ACL entries inherit into the current <tt>Acl</tt>
*/
boolean isEntriesInheriting();
/**
* Indicates whether the ACL entries from the {@link #getParentAcl()} should flow down
* into the current <tt>Acl</tt>.
* <p>
* The mere link between an <tt>Acl</tt> and a parent <tt>Acl</tt> on its own is
* insufficient to cause ACL entries to inherit down. This is because a domain object
* may wish to have entirely independent entries, but maintain the link with the
* parent for navigation purposes. Thus, this method denotes whether or not the
* navigation relationship also extends to the actual inheritance of entries.
* </p>
*
* @return <tt>true</tt> if parent ACL entries inherit into the current <tt>Acl</tt>
*/
boolean isEntriesInheriting();
/**
* This is the actual authorization logic method, and must be used whenever ACL authorization decisions are
* required.
*
* <p>An array of <tt>Sid</tt>s are presented, representing security identifies of the current
* principal. In addition, an array of <tt>Permission</tt>s is presented which will have one or more bits set
* in order to indicate the permissions needed for an affirmative authorization decision. An array is presented
* because holding <em>any</em> of the <tt>Permission</tt>s inside the array will be sufficient for an
* affirmative authorization.</p>
*
* <p>The actual approach used to make authorization decisions is left to the implementation and is not
* specified by this interface. For example, an implementation <em>MAY</em> search the current ACL in the order
* the ACL entries have been stored. If a single entry is found that has the same active bits as are shown in a
* passed <tt>Permission</tt>, that entry's grant or deny state may determine the authorization decision. If
* the case of a deny state, the deny decision will only be relevant if all other <tt>Permission</tt>s passed
* in the array have also been unsuccessfully searched. If no entry is found that match the bits in the current
* ACL, provided that {@link #isEntriesInheriting()} is <tt>true</tt>, the authorization decision may be
* passed to the parent ACL. If there is no matching entry, the implementation MAY throw an exception, or make a
* predefined authorization decision.</p>
*
* <p>This method must operate correctly even if the <tt>Acl</tt> only represents a subset of <tt>Sid</tt>s,
* although the implementation is permitted to throw one of the signature-defined exceptions if the method
* is called requesting an authorization decision for a {@link Sid} that was never loaded in this <tt>Acl</tt>.
* </p>
*
* @param permission the permission or permissions required (at least one entry required)
* @param sids the security identities held by the principal (at least one entry required)
* @param administrativeMode if <tt>true</tt> denotes the query is for administrative purposes and no logging
* or auditing (if supported by the implementation) should be undertaken
*
* @return <tt>true</tt> if authorization is granted
*
* @throws NotFoundException MUST be thrown if an implementation cannot make an authoritative authorization
* decision, usually because there is no ACL information for this particular permission and/or SID
* @throws UnloadedSidException thrown if the <tt>Acl</tt> does not have details for one or more of the
* <tt>Sid</tt>s passed as arguments
*/
boolean isGranted(List<Permission> permission, List<Sid> sids, boolean administrativeMode)
throws NotFoundException, UnloadedSidException;
/**
* This is the actual authorization logic method, and must be used whenever ACL
* authorization decisions are required.
*
* <p>
* An array of <tt>Sid</tt>s are presented, representing security identifies of the
* current principal. In addition, an array of <tt>Permission</tt>s is presented which
* will have one or more bits set in order to indicate the permissions needed for an
* affirmative authorization decision. An array is presented because holding
* <em>any</em> of the <tt>Permission</tt>s inside the array will be sufficient for an
* affirmative authorization.
* </p>
*
* <p>
* The actual approach used to make authorization decisions is left to the
* implementation and is not specified by this interface. For example, an
* implementation <em>MAY</em> search the current ACL in the order the ACL entries
* have been stored. If a single entry is found that has the same active bits as are
* shown in a passed <tt>Permission</tt>, that entry's grant or deny state may
* determine the authorization decision. If the case of a deny state, the deny
* decision will only be relevant if all other <tt>Permission</tt>s passed in the
* array have also been unsuccessfully searched. If no entry is found that match the
* bits in the current ACL, provided that {@link #isEntriesInheriting()} is
* <tt>true</tt>, the authorization decision may be passed to the parent ACL. If there
* is no matching entry, the implementation MAY throw an exception, or make a
* predefined authorization decision.
* </p>
*
* <p>
* This method must operate correctly even if the <tt>Acl</tt> only represents a
* subset of <tt>Sid</tt>s, although the implementation is permitted to throw one of
* the signature-defined exceptions if the method is called requesting an
* authorization decision for a {@link Sid} that was never loaded in this <tt>Acl</tt>
* .
* </p>
*
* @param permission the permission or permissions required (at least one entry
* required)
* @param sids the security identities held by the principal (at least one entry
* required)
* @param administrativeMode if <tt>true</tt> denotes the query is for administrative
* purposes and no logging or auditing (if supported by the implementation) should be
* undertaken
*
* @return <tt>true</tt> if authorization is granted
*
* @throws NotFoundException MUST be thrown if an implementation cannot make an
* authoritative authorization decision, usually because there is no ACL information
* for this particular permission and/or SID
* @throws UnloadedSidException thrown if the <tt>Acl</tt> does not have details for
* one or more of the <tt>Sid</tt>s passed as arguments
*/
boolean isGranted(List<Permission> permission, List<Sid> sids,
boolean administrativeMode) throws NotFoundException, UnloadedSidException;
/**
* For efficiency reasons an <tt>Acl</tt> may be loaded and <em>not</em> contain entries for every
* <tt>Sid</tt> in the system. If an <tt>Acl</tt> has been loaded and does not represent every
* <tt>Sid</tt>, all methods of the <tt>Acl</tt> can only be used within the limited scope of the
* <tt>Sid</tt> instances it actually represents.
* <p>
* It is normal to load an <tt>Acl</tt> for only particular <tt>Sid</tt>s if read-only authorization
* decisions are being made. However, if user interface reporting or modification of <tt>Acl</tt>s are
* desired, an <tt>Acl</tt> should be loaded with all <tt>Sid</tt>s. This method denotes whether or
* not the specified <tt>Sid</tt>s have been loaded or not.
* </p>
*
* @param sids one or more security identities the caller is interest in knowing whether this <tt>Sid</tt>
* supports
*
* @return <tt>true</tt> if every passed <tt>Sid</tt> is represented by this <tt>Acl</tt> instance
*/
boolean isSidLoaded(List<Sid> sids);
/**
* For efficiency reasons an <tt>Acl</tt> may be loaded and <em>not</em> contain
* entries for every <tt>Sid</tt> in the system. If an <tt>Acl</tt> has been loaded
* and does not represent every <tt>Sid</tt>, all methods of the <tt>Acl</tt> can only
* be used within the limited scope of the <tt>Sid</tt> instances it actually
* represents.
* <p>
* It is normal to load an <tt>Acl</tt> for only particular <tt>Sid</tt>s if read-only
* authorization decisions are being made. However, if user interface reporting or
* modification of <tt>Acl</tt>s are desired, an <tt>Acl</tt> should be loaded with
* all <tt>Sid</tt>s. This method denotes whether or not the specified <tt>Sid</tt>s
* have been loaded or not.
* </p>
*
* @param sids one or more security identities the caller is interest in knowing
* whether this <tt>Sid</tt> supports
*
* @return <tt>true</tt> if every passed <tt>Sid</tt> is represented by this
* <tt>Acl</tt> instance
*/
boolean isSidLoaded(List<Sid> sids);
}

View File

@ -18,7 +18,6 @@ import org.springframework.security.acls.jdbc.JdbcAclService;
import java.io.Serializable;
/**
* A caching layer for {@link JdbcAclService}.
*
@ -26,17 +25,18 @@ import java.io.Serializable;
*
*/
public interface AclCache {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
void evictFromCache(Serializable pk);
void evictFromCache(Serializable pk);
void evictFromCache(ObjectIdentity objectIdentity);
void evictFromCache(ObjectIdentity objectIdentity);
MutableAcl getFromCache(ObjectIdentity objectIdentity);
MutableAcl getFromCache(ObjectIdentity objectIdentity);
MutableAcl getFromCache(Serializable pk);
MutableAcl getFromCache(Serializable pk);
void putInCache(MutableAcl acl);
void putInCache(MutableAcl acl);
void clearCache();
void clearCache();
}

View File

@ -8,24 +8,24 @@ package org.springframework.security.acls.model;
*/
public abstract class AclDataAccessException extends RuntimeException {
/**
* Constructs an <code>AclDataAccessException</code> with the specified
* message and root cause.
*
* @param msg the detail message
* @param cause the root cause
*/
public AclDataAccessException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Constructs an <code>AclDataAccessException</code> with the specified message and
* root cause.
*
* @param msg the detail message
* @param cause the root cause
*/
public AclDataAccessException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Constructs an <code>AclDataAccessException</code> with the specified
* message and no root cause.
*
* @param msg the detail message
*/
public AclDataAccessException(String msg) {
super(msg);
}
/**
* Constructs an <code>AclDataAccessException</code> with the specified message and no
* root cause.
*
* @param msg the detail message
*/
public AclDataAccessException(String msg) {
super(msg);
}
}

View File

@ -14,85 +14,105 @@
*/
package org.springframework.security.acls.model;
import java.util.List;
import java.util.Map;
/**
* Provides retrieval of {@link Acl} instances.
*
* @author Ben Alex
*/
public interface AclService {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
/**
* Locates all object identities that use the specified parent. This is useful for administration tools.
*
* @param parentIdentity to locate children of
*
* @return the children (or <tt>null</tt> if none were found)
*/
List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity);
/**
* Locates all object identities that use the specified parent. This is useful for
* administration tools.
*
* @param parentIdentity to locate children of
*
* @return the children (or <tt>null</tt> if none were found)
*/
List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity);
/**
* Same as {@link #readAclsById(List)} except it returns only a single Acl.
* <p>
* This method should not be called as it does not leverage the underlying implementation's potential ability to
* filter <tt>Acl</tt> entries based on a {@link Sid} parameter.</p>
*
* @param object to locate an {@link Acl} for
*
* @return the {@link Acl} for the requested {@link ObjectIdentity} (never <tt>null</tt>)
*
* @throws NotFoundException if an {@link Acl} was not found for the requested {@link ObjectIdentity}
*/
Acl readAclById(ObjectIdentity object) throws NotFoundException;
/**
* Same as {@link #readAclsById(List)} except it returns only a single Acl.
* <p>
* This method should not be called as it does not leverage the underlying
* implementation's potential ability to filter <tt>Acl</tt> entries based on a
* {@link Sid} parameter.
* </p>
*
* @param object to locate an {@link Acl} for
*
* @return the {@link Acl} for the requested {@link ObjectIdentity} (never
* <tt>null</tt>)
*
* @throws NotFoundException if an {@link Acl} was not found for the requested
* {@link ObjectIdentity}
*/
Acl readAclById(ObjectIdentity object) throws NotFoundException;
/**
* Same as {@link #readAclsById(List, List)} except it returns only a single Acl.
*
* @param object to locate an {@link Acl} for
* @param sids the security identities for which {@link Acl} information is required
* (may be <tt>null</tt> to denote all entries)
*
* @return the {@link Acl} for the requested {@link ObjectIdentity} (never <tt>null</tt>)
*
* @throws NotFoundException if an {@link Acl} was not found for the requested {@link ObjectIdentity}
*/
Acl readAclById(ObjectIdentity object, List<Sid> sids) throws NotFoundException;
/**
* Same as {@link #readAclsById(List, List)} except it returns only a single Acl.
*
* @param object to locate an {@link Acl} for
* @param sids the security identities for which {@link Acl} information is required
* (may be <tt>null</tt> to denote all entries)
*
* @return the {@link Acl} for the requested {@link ObjectIdentity} (never
* <tt>null</tt>)
*
* @throws NotFoundException if an {@link Acl} was not found for the requested
* {@link ObjectIdentity}
*/
Acl readAclById(ObjectIdentity object, List<Sid> sids) throws NotFoundException;
/**
* Obtains all the <tt>Acl</tt>s that apply for the passed <tt>Object</tt>s.<p>The returned map is
* keyed on the passed objects, with the values being the <tt>Acl</tt> instances. Any unknown objects will not
* have a map key.</p>
*
* @param objects the objects to find {@link Acl} information for
*
* @return a map with exactly one element for each {@link ObjectIdentity} passed as an argument (never <tt>null</tt>)
*
* @throws NotFoundException if an {@link Acl} was not found for each requested {@link ObjectIdentity}
*/
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects) throws NotFoundException;
/**
* Obtains all the <tt>Acl</tt>s that apply for the passed <tt>Object</tt>s.
* <p>
* The returned map is keyed on the passed objects, with the values being the
* <tt>Acl</tt> instances. Any unknown objects will not have a map key.
* </p>
*
* @param objects the objects to find {@link Acl} information for
*
* @return a map with exactly one element for each {@link ObjectIdentity} passed as an
* argument (never <tt>null</tt>)
*
* @throws NotFoundException if an {@link Acl} was not found for each requested
* {@link ObjectIdentity}
*/
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects)
throws NotFoundException;
/**
* Obtains all the <tt>Acl</tt>s that apply for the passed <tt>Object</tt>s, but only for the
* security identifies passed.<p>Implementations <em>MAY</em> provide a subset of the ACLs via this method
* although this is NOT a requirement. This is intended to allow performance optimisations within implementations.
* Callers should therefore use this method in preference to the alternative overloaded version which does not
* have performance optimisation opportunities.</p>
* <p>The returned map is keyed on the passed objects, with the values being the <tt>Acl</tt>
* instances. Any unknown objects (or objects for which the interested <tt>Sid</tt>s do not have entries) will
* not have a map key.</p>
*
* @param objects the objects to find {@link Acl} information for
* @param sids the security identities for which {@link Acl} information is required
* (may be <tt>null</tt> to denote all entries)
*
* @return a map with exactly one element for each {@link ObjectIdentity} passed as an argument (never <tt>null</tt>)
*
* @throws NotFoundException if an {@link Acl} was not found for each requested {@link ObjectIdentity}
*/
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids) throws NotFoundException;
/**
* Obtains all the <tt>Acl</tt>s that apply for the passed <tt>Object</tt>s, but only
* for the security identifies passed.
* <p>
* Implementations <em>MAY</em> provide a subset of the ACLs via this method although
* this is NOT a requirement. This is intended to allow performance optimisations
* within implementations. Callers should therefore use this method in preference to
* the alternative overloaded version which does not have performance optimisation
* opportunities.
* </p>
* <p>
* The returned map is keyed on the passed objects, with the values being the
* <tt>Acl</tt> instances. Any unknown objects (or objects for which the interested
* <tt>Sid</tt>s do not have entries) will not have a map key.
* </p>
*
* @param objects the objects to find {@link Acl} information for
* @param sids the security identities for which {@link Acl} information is required
* (may be <tt>null</tt> to denote all entries)
*
* @return a map with exactly one element for each {@link ObjectIdentity} passed as an
* argument (never <tt>null</tt>)
*
* @throws NotFoundException if an {@link Acl} was not found for each requested
* {@link ObjectIdentity}
*/
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids)
throws NotFoundException;
}

View File

@ -14,32 +14,32 @@
*/
package org.springframework.security.acls.model;
/**
* Thrown if an <code>Acl</code> entry already exists for the object.
*
* @author Ben Alex
*/
public class AlreadyExistsException extends AclDataAccessException {
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
/**
* Constructs an <code>AlreadyExistsException</code> with the specified message.
*
* @param msg the detail message
*/
public AlreadyExistsException(String msg) {
super(msg);
}
/**
* Constructs an <code>AlreadyExistsException</code> with the specified message.
*
* @param msg the detail message
*/
public AlreadyExistsException(String msg) {
super(msg);
}
/**
* Constructs an <code>AlreadyExistsException</code> with the specified message
* and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public AlreadyExistsException(String msg, Throwable t) {
super(msg, t);
}
/**
* Constructs an <code>AlreadyExistsException</code> with the specified message and
* root cause.
*
* @param msg the detail message
* @param t root cause
*/
public AlreadyExistsException(String msg, Throwable t) {
super(msg, t);
}
}

View File

@ -14,7 +14,6 @@
*/
package org.springframework.security.acls.model;
/**
* Represents an ACE that provides auditing information.
*
@ -22,9 +21,10 @@ package org.springframework.security.acls.model;
*
*/
public interface AuditableAccessControlEntry extends AccessControlEntry {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
boolean isAuditFailure();
boolean isAuditFailure();
boolean isAuditSuccess();
boolean isAuditSuccess();
}

View File

@ -14,7 +14,6 @@
*/
package org.springframework.security.acls.model;
/**
* A mutable ACL that provides audit capabilities.
*
@ -22,7 +21,8 @@ package org.springframework.security.acls.model;
*
*/
public interface AuditableAcl extends MutableAcl {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
void updateAuditing(int aceIndex, boolean auditSuccess, boolean auditFailure);
void updateAuditing(int aceIndex, boolean auditSuccess, boolean auditFailure);
}

View File

@ -14,33 +14,32 @@
*/
package org.springframework.security.acls.model;
/**
* Thrown if an {@link Acl} cannot be deleted because children <code>Acl</code>s exist.
*
* @author Ben Alex
*/
public class ChildrenExistException extends AclDataAccessException {
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
/**
* Constructs an <code>ChildrenExistException</code> with the specified
* message.
*
* @param msg the detail message
*/
public ChildrenExistException(String msg) {
super(msg);
}
/**
* Constructs an <code>ChildrenExistException</code> with the specified message.
*
* @param msg the detail message
*/
public ChildrenExistException(String msg) {
super(msg);
}
/**
* Constructs an <code>ChildrenExistException</code> with the specified
* message and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public ChildrenExistException(String msg, Throwable t) {
super(msg, t);
}
/**
* Constructs an <code>ChildrenExistException</code> with the specified message and
* root cause.
*
* @param msg the detail message
* @param t root cause
*/
public ChildrenExistException(String msg, Throwable t) {
super(msg, t);
}
}

View File

@ -16,52 +16,50 @@ package org.springframework.security.acls.model;
import java.io.Serializable;
/**
* A mutable <tt>Acl</tt>.
* <p>
* A mutable ACL must ensure that appropriate security checks are performed
* before allowing access to its methods.
* A mutable ACL must ensure that appropriate security checks are performed before
* allowing access to its methods.
*
* @author Ben Alex
*/
public interface MutableAcl extends Acl {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
void deleteAce(int aceIndex) throws NotFoundException;
void deleteAce(int aceIndex) throws NotFoundException;
/**
* Obtains an identifier that represents this <tt>MutableAcl</tt>.
*
* @return the identifier, or <tt>null</tt> if unsaved
*/
Serializable getId();
/**
* Obtains an identifier that represents this <tt>MutableAcl</tt>.
*
* @return the identifier, or <tt>null</tt> if unsaved
*/
Serializable getId();
void insertAce(int atIndexLocation, Permission permission, Sid sid, boolean granting)
throws NotFoundException;
void insertAce(int atIndexLocation, Permission permission, Sid sid, boolean granting)
throws NotFoundException;
/**
* Changes the present owner to a different owner.
*
* @param newOwner the new owner (mandatory; cannot be null)
*/
void setOwner(Sid newOwner);
/**
* Changes the present owner to a different owner.
*
* @param newOwner the new owner (mandatory; cannot be null)
*/
void setOwner(Sid newOwner);
/**
* Change the value returned by {@link Acl#isEntriesInheriting()}.
*
* @param entriesInheriting the new value
*/
void setEntriesInheriting(boolean entriesInheriting);
/**
* Change the value returned by {@link Acl#isEntriesInheriting()}.
*
* @param entriesInheriting the new value
*/
void setEntriesInheriting(boolean entriesInheriting);
/**
* Changes the parent of this ACL.
*
* @param newParent the new parent
*/
void setParent(Acl newParent);
/**
* Changes the parent of this ACL.
*
* @param newParent the new parent
*/
void setParent(Acl newParent);
void updateAce(int aceIndex, Permission permission)
throws NotFoundException;
void updateAce(int aceIndex, Permission permission) throws NotFoundException;
}

View File

@ -14,48 +14,47 @@
*/
package org.springframework.security.acls.model;
/**
* Provides support for creating and storing <code>Acl</code> instances.
*
* @author Ben Alex
*/
public interface MutableAclService extends AclService {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
/**
* Creates an empty <code>Acl</code> object in the database. It will have no entries. The returned object
* will then be used to add entries.
*
* @param objectIdentity the object identity to create
*
* @return an ACL object with its ID set
*
* @throws AlreadyExistsException if the passed object identity already has a record
*/
MutableAcl createAcl(ObjectIdentity objectIdentity)
throws AlreadyExistsException;
/**
* Creates an empty <code>Acl</code> object in the database. It will have no entries.
* The returned object will then be used to add entries.
*
* @param objectIdentity the object identity to create
*
* @return an ACL object with its ID set
*
* @throws AlreadyExistsException if the passed object identity already has a record
*/
MutableAcl createAcl(ObjectIdentity objectIdentity) throws AlreadyExistsException;
/**
* Removes the specified entry from the database.
*
* @param objectIdentity the object identity to remove
* @param deleteChildren whether to cascade the delete to children
*
* @throws ChildrenExistException if the deleteChildren argument was <code>false</code> but children exist
*/
void deleteAcl(ObjectIdentity objectIdentity, boolean deleteChildren)
throws ChildrenExistException;
/**
* Removes the specified entry from the database.
*
* @param objectIdentity the object identity to remove
* @param deleteChildren whether to cascade the delete to children
*
* @throws ChildrenExistException if the deleteChildren argument was
* <code>false</code> but children exist
*/
void deleteAcl(ObjectIdentity objectIdentity, boolean deleteChildren)
throws ChildrenExistException;
/**
* Changes an existing <code>Acl</code> in the database.
*
* @param acl to modify
*
* @throws NotFoundException if the relevant record could not be found (did you remember to use {@link
* #createAcl(ObjectIdentity)} to create the object, rather than creating it with the <code>new</code>
* keyword?)
*/
MutableAcl updateAcl(MutableAcl acl) throws NotFoundException;
/**
* Changes an existing <code>Acl</code> in the database.
*
* @param acl to modify
*
* @throws NotFoundException if the relevant record could not be found (did you
* remember to use {@link #createAcl(ObjectIdentity)} to create the object, rather
* than creating it with the <code>new</code> keyword?)
*/
MutableAcl updateAcl(MutableAcl acl) throws NotFoundException;
}

View File

@ -14,32 +14,32 @@
*/
package org.springframework.security.acls.model;
/**
* Thrown if an ACL-related object cannot be found.
*
* @author Ben Alex
*/
public class NotFoundException extends AclDataAccessException {
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
/**
* Constructs an <code>NotFoundException</code> with the specified message.
*
* @param msg the detail message
*/
public NotFoundException(String msg) {
super(msg);
}
/**
* Constructs an <code>NotFoundException</code> with the specified message.
*
* @param msg the detail message
*/
public NotFoundException(String msg) {
super(msg);
}
/**
* Constructs an <code>NotFoundException</code> with the specified message
* and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public NotFoundException(String msg, Throwable t) {
super(msg, t);
}
/**
* Constructs an <code>NotFoundException</code> with the specified message and root
* cause.
*
* @param msg the detail message
* @param t root cause
*/
public NotFoundException(String msg, Throwable t) {
super(msg, t);
}
}

View File

@ -16,56 +16,59 @@ package org.springframework.security.acls.model;
import java.io.Serializable;
/**
* Represents the identity of an individual domain object instance.
*
* <p>
* As implementations of <tt>ObjectIdentity</tt> are used as the key to represent
* domain objects in the ACL subsystem, it is essential that implementations provide
* methods so that object-equality rather than reference-equality can be relied upon
* reliably. In other words, the ACL subsystem can consider two
* <tt>ObjectIdentity</tt>s equal if <tt>identity1.equals(identity2)</tt>, rather than
* reference-equality of <tt>identity1==identity2</tt>.
* As implementations of <tt>ObjectIdentity</tt> are used as the key to represent domain
* objects in the ACL subsystem, it is essential that implementations provide methods so
* that object-equality rather than reference-equality can be relied upon reliably. In
* other words, the ACL subsystem can consider two <tt>ObjectIdentity</tt>s equal if
* <tt>identity1.equals(identity2)</tt>, rather than reference-equality of
* <tt>identity1==identity2</tt>.
* </p>
*
* @author Ben Alex
*/
public interface ObjectIdentity extends Serializable {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
/**
* @param obj to be compared
*
* @return <tt>true</tt> if the objects are equal, <tt>false</tt> otherwise
* @see Object#equals(Object)
*/
boolean equals(Object obj);
/**
* @param obj to be compared
*
* @return <tt>true</tt> if the objects are equal, <tt>false</tt> otherwise
* @see Object#equals(Object)
*/
boolean equals(Object obj);
/**
* Obtains the actual identifier. This identifier must not be reused to represent other domain objects with
* the same <tt>javaType</tt>.
*
* <p>Because ACLs are largely immutable, it is strongly recommended to use
* a synthetic identifier (such as a database sequence number for the primary key). Do not use an identifier with
* business meaning, as that business meaning may change in the future such change will cascade to the ACL
* subsystem data.</p>
*
* @return the identifier (unique within this <tt>type</tt>; never <tt>null</tt>)
*/
Serializable getIdentifier();
/**
* Obtains the actual identifier. This identifier must not be reused to represent
* other domain objects with the same <tt>javaType</tt>.
*
* <p>
* Because ACLs are largely immutable, it is strongly recommended to use a synthetic
* identifier (such as a database sequence number for the primary key). Do not use an
* identifier with business meaning, as that business meaning may change in the future
* such change will cascade to the ACL subsystem data.
* </p>
*
* @return the identifier (unique within this <tt>type</tt>; never <tt>null</tt>)
*/
Serializable getIdentifier();
/**
* Obtains the "type" metadata for the domain object. This will often be a Java type name (an interface or a class)
* &ndash; traditionally it is the name of the domain object implementation class.
*
* @return the "type" of the domain object (never <tt>null</tt>).
*/
String getType();
/**
* Obtains the "type" metadata for the domain object. This will often be a Java type
* name (an interface or a class) &ndash; traditionally it is the name of the domain
* object implementation class.
*
* @return the "type" of the domain object (never <tt>null</tt>).
*/
String getType();
/**
* @return a hash code representation of the <tt>ObjectIdentity</tt>
* @see Object#hashCode()
*/
int hashCode();
/**
* @return a hash code representation of the <tt>ObjectIdentity</tt>
* @see Object#hashCode()
*/
int hashCode();
}

View File

@ -2,25 +2,25 @@ package org.springframework.security.acls.model;
import java.io.Serializable;
/**
* Strategy which creates an {@link ObjectIdentity} from an object identifier (such as a primary key)
* and type information.
* Strategy which creates an {@link ObjectIdentity} from an object identifier (such as a
* primary key) and type information.
* <p>
* Differs from {@link ObjectIdentityRetrievalStrategy} in that it is used in situations when the actual object
* instance isn't available.
* Differs from {@link ObjectIdentityRetrievalStrategy} in that it is used in situations
* when the actual object instance isn't available.
*
* @author Luke Taylor
* @since 3.0
*/
public interface ObjectIdentityGenerator {
/**
*
* @param id the identifier of the domain object, not null
* @param type the type of the object (often a class name), not null
* @return the identity constructed using the supplied identifier and type information.
*/
ObjectIdentity createObjectIdentity(Serializable id, String type);
/**
*
* @param id the identifier of the domain object, not null
* @param type the type of the object (often a class name), not null
* @return the identity constructed using the supplied identifier and type
* information.
*/
ObjectIdentity createObjectIdentity(Serializable id, String type);
}

View File

@ -15,7 +15,6 @@
package org.springframework.security.acls.model;
/**
* Strategy interface that provides the ability to determine which {@link ObjectIdentity}
* will be returned for a particular domain object
@ -24,7 +23,8 @@ package org.springframework.security.acls.model;
*
*/
public interface ObjectIdentityRetrievalStrategy {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
ObjectIdentity getObjectIdentity(Object domainObject);
ObjectIdentity getObjectIdentity(Object domainObject);
}

View File

@ -14,19 +14,18 @@
*/
package org.springframework.security.acls.model;
/**
* A mutable ACL that provides ownership capabilities.
*
* <p>
* Generally the owner of an ACL is able to call any ACL mutator method, as
* well as assign a new owner.
* Generally the owner of an ACL is able to call any ACL mutator method, as well as assign
* a new owner.
*
* @author Ben Alex
*/
public interface OwnershipAcl extends MutableAcl {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
void setOwner(Sid newOwner);
void setOwner(Sid newOwner);
}

View File

@ -22,36 +22,41 @@ import java.io.Serializable;
* @author Ben Alex
*/
public interface Permission extends Serializable {
//~ Static fields/initializers =====================================================================================
// ~ Static fields/initializers
// =====================================================================================
char RESERVED_ON = '~';
char RESERVED_OFF = '.';
String THIRTY_TWO_RESERVED_OFF = "................................";
char RESERVED_ON = '~';
char RESERVED_OFF = '.';
String THIRTY_TWO_RESERVED_OFF = "................................";
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
/**
* Returns the bits that represents the permission.
*
* @return the bits that represent the permission
*/
int getMask();
/**
* Returns the bits that represents the permission.
*
* @return the bits that represent the permission
*/
int getMask();
/**
* Returns a 32-character long bit pattern <code>String</code> representing this permission.
* <p>
* Implementations are free to format the pattern as they see fit, although under no circumstances may
* {@link #RESERVED_OFF} or {@link #RESERVED_ON} be used within the pattern. An exemption is in the case of
* {@link #RESERVED_OFF} which is used to denote a bit that is off (clear).
* Implementations may also elect to use {@link #RESERVED_ON} internally for computation purposes,
* although this method may not return any <code>String</code> containing {@link #RESERVED_ON}.
* <p>
* The returned String must be 32 characters in length.
* <p>
* This method is only used for user interface and logging purposes. It is not used in any permission
* calculations. Therefore, duplication of characters within the output is permitted.
*
* @return a 32-character bit pattern
*/
String getPattern();
/**
* Returns a 32-character long bit pattern <code>String</code> representing this
* permission.
* <p>
* Implementations are free to format the pattern as they see fit, although under no
* circumstances may {@link #RESERVED_OFF} or {@link #RESERVED_ON} be used within the
* pattern. An exemption is in the case of {@link #RESERVED_OFF} which is used to
* denote a bit that is off (clear). Implementations may also elect to use
* {@link #RESERVED_ON} internally for computation purposes, although this method may
* not return any <code>String</code> containing {@link #RESERVED_ON}.
* <p>
* The returned String must be 32 characters in length.
* <p>
* This method is only used for user interface and logging purposes. It is not used in
* any permission calculations. Therefore, duplication of characters within the output
* is permitted.
*
* @return a 32-character bit pattern
*/
String getPattern();
}

View File

@ -3,18 +3,19 @@ package org.springframework.security.acls.model;
import java.util.List;
/**
* Allow customization of the logic for determining whether a permission or permissions are granted to a particular
* sid or sids by an {@link Acl}.
* Allow customization of the logic for determining whether a permission or permissions
* are granted to a particular sid or sids by an {@link Acl}.
*
* @author Luke Taylor
* @since 3.0.2
*/
public interface PermissionGrantingStrategy {
/**
* Returns true if the the supplied strategy decides that the supplied {@code Acl} grants access
* based on the supplied list of permissions and sids.
*/
boolean isGranted(Acl acl, List<Permission> permission, List<Sid> sids, boolean administrativeMode);
/**
* Returns true if the the supplied strategy decides that the supplied {@code Acl}
* grants access based on the supplied list of permissions and sids.
*/
boolean isGranted(Acl acl, List<Permission> permission, List<Sid> sids,
boolean administrativeMode);
}

View File

@ -20,32 +20,34 @@ import java.io.Serializable;
* A security identity recognised by the ACL system.
*
* <p>
* This interface provides indirection between actual security objects (eg
* principals, roles, groups etc) and what is stored inside an
* <code>Acl</code>. This is because an <code>Acl</code> will not store an
* entire security object, but only an abstraction of it. This interface
* therefore provides a simple way to compare these abstracted security
* This interface provides indirection between actual security objects (eg principals,
* roles, groups etc) and what is stored inside an <code>Acl</code>. This is because an
* <code>Acl</code> will not store an entire security object, but only an abstraction of
* it. This interface therefore provides a simple way to compare these abstracted security
* identities with other security identities and actual security objects.
* </p>
*
* @author Ben Alex
*/
public interface Sid extends Serializable {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
/**
* Refer to the <code>java.lang.Object</code> documentation for the interface contract.
*
* @param obj to be compared
*
* @return <code>true</code> if the objects are equal, <code>false</code> otherwise
*/
boolean equals(Object obj);
/**
* Refer to the <code>java.lang.Object</code> documentation for the interface
* contract.
*
* @param obj to be compared
*
* @return <code>true</code> if the objects are equal, <code>false</code> otherwise
*/
boolean equals(Object obj);
/**
* Refer to the <code>java.lang.Object</code> documentation for the interface contract.
*
* @return a hash code representation of this object
*/
int hashCode();
/**
* Refer to the <code>java.lang.Object</code> documentation for the interface
* contract.
*
* @return a hash code representation of this object
*/
int hashCode();
}

View File

@ -19,15 +19,15 @@ import java.util.List;
import org.springframework.security.core.Authentication;
/**
* Strategy interface that provides an ability to determine the {@link Sid} instances applicable
* for an {@link Authentication}.
* Strategy interface that provides an ability to determine the {@link Sid} instances
* applicable for an {@link Authentication}.
*
* @author Ben Alex
*/
public interface SidRetrievalStrategy {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
List<Sid> getSids(Authentication authentication);
List<Sid> getSids(Authentication authentication);
}

View File

@ -14,33 +14,34 @@
*/
package org.springframework.security.acls.model;
/**
* Thrown if an {@link Acl} cannot perform an operation because it only loaded a subset of <code>Sid</code>s and
* the caller has requested details for an unloaded <code>Sid</code>.
* Thrown if an {@link Acl} cannot perform an operation because it only loaded a subset of
* <code>Sid</code>s and the caller has requested details for an unloaded <code>Sid</code>
* .
*
* @author Ben Alex
*/
public class UnloadedSidException extends AclDataAccessException {
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
/**
* Constructs an <code>NotFoundException</code> with the specified message.
*
* @param msg the detail message
*/
public UnloadedSidException(String msg) {
super(msg);
}
/**
* Constructs an <code>NotFoundException</code> with the specified message.
*
* @param msg the detail message
*/
public UnloadedSidException(String msg) {
super(msg);
}
/**
* Constructs an <code>NotFoundException</code> with the specified message
* and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public UnloadedSidException(String msg, Throwable t) {
super(msg, t);
}
/**
* Constructs an <code>NotFoundException</code> with the specified message and root
* cause.
*
* @param msg the detail message
* @param t root cause
*/
public UnloadedSidException(String msg, Throwable t) {
super(msg, t);
}
}

View File

@ -13,123 +13,132 @@ import junit.framework.TestCase;
*/
public class AclFormattingUtilsTests extends TestCase {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public final void testDemergePatternsParametersConstraints() throws Exception {
try {
AclFormattingUtils.demergePatterns(null, "SOME STRING");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
public final void testDemergePatternsParametersConstraints() throws Exception {
try {
AclFormattingUtils.demergePatterns(null, "SOME STRING");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.demergePatterns("SOME STRING", null);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.demergePatterns("SOME STRING", null);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.demergePatterns("SOME STRING", "LONGER SOME STRING");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.demergePatterns("SOME STRING", "LONGER SOME STRING");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.demergePatterns("SOME STRING", "SAME LENGTH");
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
}
try {
AclFormattingUtils.demergePatterns("SOME STRING", "SAME LENGTH");
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
}
public final void testDemergePatterns() throws Exception {
String original = "...........................A...R";
String removeBits = "...............................R";
Assert.assertEquals("...........................A....", AclFormattingUtils
.demergePatterns(original, removeBits));
public final void testDemergePatterns() throws Exception {
String original = "...........................A...R";
String removeBits = "...............................R";
Assert.assertEquals("...........................A....",
AclFormattingUtils.demergePatterns(original, removeBits));
Assert.assertEquals("ABCDEF", AclFormattingUtils.demergePatterns("ABCDEF", "......"));
Assert.assertEquals("......", AclFormattingUtils.demergePatterns("ABCDEF", "GHIJKL"));
}
Assert.assertEquals("ABCDEF",
AclFormattingUtils.demergePatterns("ABCDEF", "......"));
Assert.assertEquals("......",
AclFormattingUtils.demergePatterns("ABCDEF", "GHIJKL"));
}
public final void testMergePatternsParametersConstraints() throws Exception {
try {
AclFormattingUtils.mergePatterns(null, "SOME STRING");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
public final void testMergePatternsParametersConstraints() throws Exception {
try {
AclFormattingUtils.mergePatterns(null, "SOME STRING");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.mergePatterns("SOME STRING", null);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.mergePatterns("SOME STRING", null);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.mergePatterns("SOME STRING", "LONGER SOME STRING");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.mergePatterns("SOME STRING", "LONGER SOME STRING");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.mergePatterns("SOME STRING", "SAME LENGTH");
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
}
try {
AclFormattingUtils.mergePatterns("SOME STRING", "SAME LENGTH");
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
}
public final void testMergePatterns() throws Exception {
String original = "...............................R";
String extraBits = "...........................A....";
Assert.assertEquals("...........................A...R", AclFormattingUtils
.mergePatterns(original, extraBits));
public final void testMergePatterns() throws Exception {
String original = "...............................R";
String extraBits = "...........................A....";
Assert.assertEquals("...........................A...R",
AclFormattingUtils.mergePatterns(original, extraBits));
Assert.assertEquals("ABCDEF", AclFormattingUtils.mergePatterns("ABCDEF", "......"));
Assert.assertEquals("GHIJKL", AclFormattingUtils.mergePatterns("ABCDEF", "GHIJKL"));
}
Assert.assertEquals("ABCDEF",
AclFormattingUtils.mergePatterns("ABCDEF", "......"));
Assert.assertEquals("GHIJKL",
AclFormattingUtils.mergePatterns("ABCDEF", "GHIJKL"));
}
public final void testBinaryPrints() throws Exception {
Assert.assertEquals("............................****", AclFormattingUtils.printBinary(15));
public final void testBinaryPrints() throws Exception {
Assert.assertEquals("............................****",
AclFormattingUtils.printBinary(15));
try {
AclFormattingUtils.printBinary(15, Permission.RESERVED_ON);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException notExpected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.printBinary(15, Permission.RESERVED_ON);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException notExpected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.printBinary(15, Permission.RESERVED_OFF);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException notExpected) {
Assert.assertTrue(true);
}
try {
AclFormattingUtils.printBinary(15, Permission.RESERVED_OFF);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException notExpected) {
Assert.assertTrue(true);
}
Assert.assertEquals("............................xxxx", AclFormattingUtils.printBinary(15, 'x'));
}
Assert.assertEquals("............................xxxx",
AclFormattingUtils.printBinary(15, 'x'));
}
public void testPrintBinaryNegative() {
Assert.assertEquals("*...............................", AclFormattingUtils.printBinary(0x80000000));
}
public void testPrintBinaryNegative() {
Assert.assertEquals("*...............................",
AclFormattingUtils.printBinary(0x80000000));
}
public void testPrintBinaryMinusOne() {
Assert.assertEquals("********************************", AclFormattingUtils.printBinary(0xffffffff));
}
public void testPrintBinaryMinusOne() {
Assert.assertEquals("********************************",
AclFormattingUtils.printBinary(0xffffffff));
}
}

View File

@ -17,40 +17,41 @@ import java.util.List;
/**
* @author Luke Taylor
*/
@SuppressWarnings({"unchecked"})
@SuppressWarnings({ "unchecked" })
public class AclPermissionCacheOptimizerTests {
@Test
public void eagerlyLoadsRequiredAcls() throws Exception {
AclService service = mock(AclService.class);
AclPermissionCacheOptimizer pco = new AclPermissionCacheOptimizer(service);
ObjectIdentityRetrievalStrategy oidStrat = mock(ObjectIdentityRetrievalStrategy.class);
SidRetrievalStrategy sidStrat = mock(SidRetrievalStrategy.class);
pco.setObjectIdentityRetrievalStrategy(oidStrat);
pco.setSidRetrievalStrategy(sidStrat);
Object[] dos = {new Object(), null, new Object()};
ObjectIdentity[] oids = {new ObjectIdentityImpl("A", "1"), new ObjectIdentityImpl("A", "2")};
when(oidStrat.getObjectIdentity(dos[0])).thenReturn(oids[0]);
when(oidStrat.getObjectIdentity(dos[2])).thenReturn(oids[1]);
@Test
public void eagerlyLoadsRequiredAcls() throws Exception {
AclService service = mock(AclService.class);
AclPermissionCacheOptimizer pco = new AclPermissionCacheOptimizer(service);
ObjectIdentityRetrievalStrategy oidStrat = mock(ObjectIdentityRetrievalStrategy.class);
SidRetrievalStrategy sidStrat = mock(SidRetrievalStrategy.class);
pco.setObjectIdentityRetrievalStrategy(oidStrat);
pco.setSidRetrievalStrategy(sidStrat);
Object[] dos = { new Object(), null, new Object() };
ObjectIdentity[] oids = { new ObjectIdentityImpl("A", "1"),
new ObjectIdentityImpl("A", "2") };
when(oidStrat.getObjectIdentity(dos[0])).thenReturn(oids[0]);
when(oidStrat.getObjectIdentity(dos[2])).thenReturn(oids[1]);
pco.cachePermissionsFor(mock(Authentication.class), Arrays.asList(dos));
pco.cachePermissionsFor(mock(Authentication.class), Arrays.asList(dos));
// AclService should be invoked with the list of required Oids
verify(service).readAclsById(eq(Arrays.asList(oids)), any(List.class));
}
// AclService should be invoked with the list of required Oids
verify(service).readAclsById(eq(Arrays.asList(oids)), any(List.class));
}
@Test
public void ignoresEmptyCollection() {
AclService service = mock(AclService.class);
AclPermissionCacheOptimizer pco = new AclPermissionCacheOptimizer(service);
ObjectIdentityRetrievalStrategy oids = mock(ObjectIdentityRetrievalStrategy.class);
SidRetrievalStrategy sids = mock(SidRetrievalStrategy.class);
pco.setObjectIdentityRetrievalStrategy(oids);
pco.setSidRetrievalStrategy(sids);
@Test
public void ignoresEmptyCollection() {
AclService service = mock(AclService.class);
AclPermissionCacheOptimizer pco = new AclPermissionCacheOptimizer(service);
ObjectIdentityRetrievalStrategy oids = mock(ObjectIdentityRetrievalStrategy.class);
SidRetrievalStrategy sids = mock(SidRetrievalStrategy.class);
pco.setObjectIdentityRetrievalStrategy(oids);
pco.setSidRetrievalStrategy(sids);
pco.cachePermissionsFor(mock(Authentication.class), Collections.emptyList());
pco.cachePermissionsFor(mock(Authentication.class), Collections.emptyList());
verifyZeroInteractions(service, sids, oids);
}
verifyZeroInteractions(service, sids, oids);
}
}

View File

@ -19,21 +19,21 @@ import org.springframework.security.core.Authentication;
*/
public class AclPermissionEvaluatorTests {
@Test
@SuppressWarnings("unchecked")
public void hasPermissionReturnsTrueIfAclGrantsPermission() throws Exception {
AclService service = mock(AclService.class);
AclPermissionEvaluator pe = new AclPermissionEvaluator(service);
ObjectIdentity oid = mock(ObjectIdentity.class);
ObjectIdentityRetrievalStrategy oidStrategy = mock(ObjectIdentityRetrievalStrategy.class);
when(oidStrategy.getObjectIdentity(anyObject())).thenReturn(oid);
pe.setObjectIdentityRetrievalStrategy(oidStrategy);
pe.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
Acl acl = mock(Acl.class);
@Test
@SuppressWarnings("unchecked")
public void hasPermissionReturnsTrueIfAclGrantsPermission() throws Exception {
AclService service = mock(AclService.class);
AclPermissionEvaluator pe = new AclPermissionEvaluator(service);
ObjectIdentity oid = mock(ObjectIdentity.class);
ObjectIdentityRetrievalStrategy oidStrategy = mock(ObjectIdentityRetrievalStrategy.class);
when(oidStrategy.getObjectIdentity(anyObject())).thenReturn(oid);
pe.setObjectIdentityRetrievalStrategy(oidStrategy);
pe.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
Acl acl = mock(Acl.class);
when(service.readAclById(any(ObjectIdentity.class), anyList())).thenReturn(acl);
when(acl.isGranted(anyList(), anyList(), eq(false))).thenReturn(true);
when(service.readAclById(any(ObjectIdentity.class), anyList())).thenReturn(acl);
when(acl.isGranted(anyList(), anyList(), eq(false))).thenReturn(true);
assertTrue(pe.hasPermission(mock(Authentication.class), new Object(), "READ"));
}
assertTrue(pe.hasPermission(mock(Authentication.class), new Object(), "READ"));
}
}

View File

@ -23,42 +23,55 @@ import java.util.List;
/**
* @author Luke Taylor
*/
@SuppressWarnings({"unchecked"})
@SuppressWarnings({ "unchecked" })
public class AclEntryAfterInvocationCollectionFilteringProviderTests {
@Test
public void objectsAreRemovedIfPermissionDenied() throws Exception {
AclService service = mock(AclService.class);
Acl acl = mock(Acl.class);
when(acl.isGranted(any(List.class), any(List.class), anyBoolean())).thenReturn(false);
when(service.readAclById(any(ObjectIdentity.class), any(List.class))).thenReturn(acl);
AclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(service, Arrays.asList(mock(Permission.class)));
provider.setObjectIdentityRetrievalStrategy(mock(ObjectIdentityRetrievalStrategy.class));
provider.setProcessDomainObjectClass(Object.class);
provider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
@Test
public void objectsAreRemovedIfPermissionDenied() throws Exception {
AclService service = mock(AclService.class);
Acl acl = mock(Acl.class);
when(acl.isGranted(any(List.class), any(List.class), anyBoolean())).thenReturn(
false);
when(service.readAclById(any(ObjectIdentity.class), any(List.class))).thenReturn(
acl);
AclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(
service, Arrays.asList(mock(Permission.class)));
provider.setObjectIdentityRetrievalStrategy(mock(ObjectIdentityRetrievalStrategy.class));
provider.setProcessDomainObjectClass(Object.class);
provider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
Object returned = provider.decide(mock(Authentication.class), new Object(), SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"), new ArrayList(Arrays.asList(new Object(), new Object())));
assertTrue(returned instanceof List);
assertTrue(((List)returned).isEmpty());
returned = provider.decide(mock(Authentication.class), new Object(), SecurityConfig.createList("UNSUPPORTED", "AFTER_ACL_COLLECTION_READ"), new Object[] {new Object(), new Object()});
assertTrue(returned instanceof Object[]);
assertTrue(((Object[])returned).length == 0);
}
Object returned = provider.decide(mock(Authentication.class), new Object(),
SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"), new ArrayList(
Arrays.asList(new Object(), new Object())));
assertTrue(returned instanceof List);
assertTrue(((List) returned).isEmpty());
returned = provider.decide(mock(Authentication.class), new Object(),
SecurityConfig.createList("UNSUPPORTED", "AFTER_ACL_COLLECTION_READ"),
new Object[] { new Object(), new Object() });
assertTrue(returned instanceof Object[]);
assertTrue(((Object[]) returned).length == 0);
}
@Test
public void accessIsGrantedIfNoAttributesDefined() throws Exception {
AclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(mock(AclService.class), Arrays.asList(mock(Permission.class)));
Object returned = new Object();
@Test
public void accessIsGrantedIfNoAttributesDefined() throws Exception {
AclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(
mock(AclService.class), Arrays.asList(mock(Permission.class)));
Object returned = new Object();
assertSame(returned, provider.decide(mock(Authentication.class), new Object(), Collections.<ConfigAttribute>emptyList(), returned));
}
assertSame(
returned,
provider.decide(mock(Authentication.class), new Object(),
Collections.<ConfigAttribute> emptyList(), returned));
}
@Test
public void nullReturnObjectIsIgnored() throws Exception {
AclService service = mock(AclService.class);
AclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(service, Arrays.asList(mock(Permission.class)));
@Test
public void nullReturnObjectIsIgnored() throws Exception {
AclService service = mock(AclService.class);
AclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(
service, Arrays.asList(mock(Permission.class)));
assertNull(provider.decide(mock(Authentication.class), new Object(), SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"), null));
verify(service, never()).readAclById(any(ObjectIdentity.class), any(List.class));
}
assertNull(provider.decide(mock(Authentication.class), new Object(),
SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"), null));
verify(service, never()).readAclById(any(ObjectIdentity.class), any(List.class));
}
}

View File

@ -19,83 +19,108 @@ import java.util.List;
/**
* @author Luke Taylor
*/
@SuppressWarnings({"unchecked"})
@SuppressWarnings({ "unchecked" })
public class AclEntryAfterInvocationProviderTests {
@Test(expected=IllegalArgumentException.class)
public void rejectsMissingPermissions() throws Exception {
try {
new AclEntryAfterInvocationProvider(mock(AclService.class), null);
fail("Exception expected");
} catch (IllegalArgumentException expected) {
}
new AclEntryAfterInvocationProvider(mock(AclService.class), Collections.<Permission>emptyList());
}
@Test(expected = IllegalArgumentException.class)
public void rejectsMissingPermissions() throws Exception {
try {
new AclEntryAfterInvocationProvider(mock(AclService.class), null);
fail("Exception expected");
}
catch (IllegalArgumentException expected) {
}
new AclEntryAfterInvocationProvider(mock(AclService.class),
Collections.<Permission> emptyList());
}
@Test
public void accessIsAllowedIfPermissionIsGranted() {
AclService service = mock(AclService.class);
Acl acl = mock(Acl.class);
when(acl.isGranted(any(List.class), any(List.class), anyBoolean())).thenReturn(true);
when(service.readAclById(any(ObjectIdentity.class), any(List.class))).thenReturn(acl);
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(service, Arrays.asList(mock(Permission.class)));
provider.setMessageSource(new SpringSecurityMessageSource());
provider.setObjectIdentityRetrievalStrategy(mock(ObjectIdentityRetrievalStrategy.class));
provider.setProcessDomainObjectClass(Object.class);
provider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
Object returned = new Object();
@Test
public void accessIsAllowedIfPermissionIsGranted() {
AclService service = mock(AclService.class);
Acl acl = mock(Acl.class);
when(acl.isGranted(any(List.class), any(List.class), anyBoolean())).thenReturn(
true);
when(service.readAclById(any(ObjectIdentity.class), any(List.class))).thenReturn(
acl);
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(
service, Arrays.asList(mock(Permission.class)));
provider.setMessageSource(new SpringSecurityMessageSource());
provider.setObjectIdentityRetrievalStrategy(mock(ObjectIdentityRetrievalStrategy.class));
provider.setProcessDomainObjectClass(Object.class);
provider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
Object returned = new Object();
assertSame(returned, provider.decide(mock(Authentication.class), new Object(), SecurityConfig.createList("AFTER_ACL_READ"), returned));
}
assertSame(
returned,
provider.decide(mock(Authentication.class), new Object(),
SecurityConfig.createList("AFTER_ACL_READ"), returned));
}
@Test
public void accessIsGrantedIfNoAttributesDefined() throws Exception {
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(mock(AclService.class), Arrays.asList(mock(Permission.class)));
Object returned = new Object();
@Test
public void accessIsGrantedIfNoAttributesDefined() throws Exception {
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(
mock(AclService.class), Arrays.asList(mock(Permission.class)));
Object returned = new Object();
assertSame(returned, provider.decide(mock(Authentication.class), new Object(), Collections.<ConfigAttribute>emptyList(), returned));
}
assertSame(
returned,
provider.decide(mock(Authentication.class), new Object(),
Collections.<ConfigAttribute> emptyList(), returned));
}
@Test
public void accessIsGrantedIfObjectTypeNotSupported() throws Exception {
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(mock(AclService.class), Arrays.asList(mock(Permission.class)));
provider.setProcessDomainObjectClass(String.class);
// Not a String
Object returned = new Object();
@Test
public void accessIsGrantedIfObjectTypeNotSupported() throws Exception {
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(
mock(AclService.class), Arrays.asList(mock(Permission.class)));
provider.setProcessDomainObjectClass(String.class);
// Not a String
Object returned = new Object();
assertSame(returned, provider.decide(mock(Authentication.class), new Object(), SecurityConfig.createList("AFTER_ACL_READ"), returned));
}
assertSame(
returned,
provider.decide(mock(Authentication.class), new Object(),
SecurityConfig.createList("AFTER_ACL_READ"), returned));
}
@Test(expected = AccessDeniedException.class)
public void accessIsDeniedIfPermissionIsNotGranted() {
AclService service = mock(AclService.class);
Acl acl = mock(Acl.class);
when(acl.isGranted(any(List.class), any(List.class), anyBoolean())).thenReturn(
false);
// Try a second time with no permissions found
when(acl.isGranted(any(List.class), any(List.class), anyBoolean())).thenThrow(
new NotFoundException(""));
when(service.readAclById(any(ObjectIdentity.class), any(List.class))).thenReturn(
acl);
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(
service, Arrays.asList(mock(Permission.class)));
provider.setProcessConfigAttribute("MY_ATTRIBUTE");
provider.setMessageSource(new SpringSecurityMessageSource());
provider.setObjectIdentityRetrievalStrategy(mock(ObjectIdentityRetrievalStrategy.class));
provider.setProcessDomainObjectClass(Object.class);
provider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
try {
provider.decide(mock(Authentication.class), new Object(),
SecurityConfig.createList("UNSUPPORTED", "MY_ATTRIBUTE"),
new Object());
fail();
}
catch (AccessDeniedException expected) {
}
// Second scenario with no acls found
provider.decide(mock(Authentication.class), new Object(),
SecurityConfig.createList("UNSUPPORTED", "MY_ATTRIBUTE"), new Object());
}
@Test(expected= AccessDeniedException.class)
public void accessIsDeniedIfPermissionIsNotGranted() {
AclService service = mock(AclService.class);
Acl acl = mock(Acl.class);
when(acl.isGranted(any(List.class), any(List.class), anyBoolean())).thenReturn(false);
// Try a second time with no permissions found
when(acl.isGranted(any(List.class), any(List.class), anyBoolean())).thenThrow(new NotFoundException(""));
when(service.readAclById(any(ObjectIdentity.class), any(List.class))).thenReturn(acl);
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(service, Arrays.asList(mock(Permission.class)));
provider.setProcessConfigAttribute("MY_ATTRIBUTE");
provider.setMessageSource(new SpringSecurityMessageSource());
provider.setObjectIdentityRetrievalStrategy(mock(ObjectIdentityRetrievalStrategy.class));
provider.setProcessDomainObjectClass(Object.class);
provider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
try {
provider.decide(mock(Authentication.class), new Object(), SecurityConfig.createList("UNSUPPORTED", "MY_ATTRIBUTE"), new Object());
fail();
} catch (AccessDeniedException expected) {
}
// Second scenario with no acls found
provider.decide(mock(Authentication.class), new Object(), SecurityConfig.createList("UNSUPPORTED", "MY_ATTRIBUTE"), new Object());
}
@Test
public void nullReturnObjectIsIgnored() throws Exception {
AclService service = mock(AclService.class);
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(
service, Arrays.asList(mock(Permission.class)));
@Test
public void nullReturnObjectIsIgnored() throws Exception {
AclService service = mock(AclService.class);
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(service, Arrays.asList(mock(Permission.class)));
assertNull(provider.decide(mock(Authentication.class), new Object(), SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"), null));
verify(service, never()).readAclById(any(ObjectIdentity.class), any(List.class));
}
assertNull(provider.decide(mock(Authentication.class), new Object(),
SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"), null));
verify(service, never()).readAclById(any(ObjectIdentity.class), any(List.class));
}
}

View File

@ -17,84 +17,86 @@ import org.springframework.security.acls.model.Sid;
*/
public class AccessControlImplEntryTests {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
@Test
public void testConstructorRequiredFields() {
// Check Acl field is present
try {
new AccessControlEntryImpl(null, null, new PrincipalSid("johndoe"),
BasePermission.ADMINISTRATION, true, true, true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
@Test
public void testConstructorRequiredFields() {
// Check Acl field is present
try {
new AccessControlEntryImpl(null, null, new PrincipalSid("johndoe"),
BasePermission.ADMINISTRATION, true, true, true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// Check Sid field is present
try {
new AccessControlEntryImpl(null, mock(Acl.class), null,
BasePermission.ADMINISTRATION, true, true, true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// Check Sid field is present
try {
new AccessControlEntryImpl(null, mock(Acl.class), null,
BasePermission.ADMINISTRATION, true, true, true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// Check Permission field is present
try {
new AccessControlEntryImpl(null, mock(Acl.class), new PrincipalSid("johndoe"), null,
true, true, true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
// Check Permission field is present
try {
new AccessControlEntryImpl(null, mock(Acl.class),
new PrincipalSid("johndoe"), null, true, true, true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
@Test
public void testAccessControlEntryImplGetters() {
Acl mockAcl = mock(Acl.class);
Sid sid = new PrincipalSid("johndoe");
@Test
public void testAccessControlEntryImplGetters() {
Acl mockAcl = mock(Acl.class);
Sid sid = new PrincipalSid("johndoe");
// Create a sample entry
AccessControlEntry ace = new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid, BasePermission.ADMINISTRATION,
true, true, true);
// Create a sample entry
AccessControlEntry ace = new AccessControlEntryImpl(Long.valueOf(1), mockAcl,
sid, BasePermission.ADMINISTRATION, true, true, true);
// and check every get() method
assertEquals(new Long(1), ace.getId());
assertEquals(mockAcl, ace.getAcl());
assertEquals(sid, ace.getSid());
assertTrue(ace.isGranting());
assertEquals(BasePermission.ADMINISTRATION, ace.getPermission());
assertTrue(((AuditableAccessControlEntry) ace).isAuditFailure());
assertTrue(((AuditableAccessControlEntry) ace).isAuditSuccess());
}
// and check every get() method
assertEquals(new Long(1), ace.getId());
assertEquals(mockAcl, ace.getAcl());
assertEquals(sid, ace.getSid());
assertTrue(ace.isGranting());
assertEquals(BasePermission.ADMINISTRATION, ace.getPermission());
assertTrue(((AuditableAccessControlEntry) ace).isAuditFailure());
assertTrue(((AuditableAccessControlEntry) ace).isAuditSuccess());
}
@Test
public void testEquals() {
final Acl mockAcl = mock(Acl.class);
final ObjectIdentity oid = mock(ObjectIdentity.class);
@Test
public void testEquals() {
final Acl mockAcl = mock(Acl.class);
final ObjectIdentity oid = mock(ObjectIdentity.class);
when(mockAcl.getObjectIdentity()).thenReturn(oid);
Sid sid = new PrincipalSid("johndoe");
when(mockAcl.getObjectIdentity()).thenReturn(oid);
Sid sid = new PrincipalSid("johndoe");
AccessControlEntry ace = new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid, BasePermission.ADMINISTRATION,
true, true, true);
AccessControlEntry ace = new AccessControlEntryImpl(Long.valueOf(1), mockAcl,
sid, BasePermission.ADMINISTRATION, true, true, true);
assertFalse(ace.equals(null));
assertFalse(ace.equals(Long.valueOf(100)));
assertTrue(ace.equals(ace));
assertTrue(ace.equals(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid,
BasePermission.ADMINISTRATION, true, true, true)));
assertFalse(ace.equals(new AccessControlEntryImpl(Long.valueOf(2), mockAcl, sid,
BasePermission.ADMINISTRATION, true, true, true)));
assertFalse(ace.equals(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, new PrincipalSid("scott"),
BasePermission.ADMINISTRATION, true, true, true)));
assertFalse(ace.equals(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid, BasePermission.WRITE, true,
true, true)));
assertFalse(ace.equals(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid,
BasePermission.ADMINISTRATION, false, true, true)));
assertFalse(ace.equals(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid,
BasePermission.ADMINISTRATION, true, false, true)));
assertFalse(ace.equals(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid,
BasePermission.ADMINISTRATION, true, true, false)));
}
assertFalse(ace.equals(null));
assertFalse(ace.equals(Long.valueOf(100)));
assertTrue(ace.equals(ace));
assertTrue(ace.equals(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid,
BasePermission.ADMINISTRATION, true, true, true)));
assertFalse(ace.equals(new AccessControlEntryImpl(Long.valueOf(2), mockAcl, sid,
BasePermission.ADMINISTRATION, true, true, true)));
assertFalse(ace.equals(new AccessControlEntryImpl(Long.valueOf(1), mockAcl,
new PrincipalSid("scott"), BasePermission.ADMINISTRATION, true, true,
true)));
assertFalse(ace.equals(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid,
BasePermission.WRITE, true, true, true)));
assertFalse(ace.equals(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid,
BasePermission.ADMINISTRATION, false, true, true)));
assertFalse(ace.equals(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid,
BasePermission.ADMINISTRATION, true, false, true)));
assertFalse(ace.equals(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid,
BasePermission.ADMINISTRATION, true, true, false)));
}
}

View File

@ -14,245 +14,284 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* Test class for {@link AclAuthorizationStrategyImpl} and {@link AclImpl}
* security checks.
* Test class for {@link AclAuthorizationStrategyImpl} and {@link AclImpl} security
* checks.
*
* @author Andrei Stefan
*/
public class AclImplementationSecurityCheckTests {
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
@Before
public void setUp() throws Exception {
SecurityContextHolder.clearContext();
}
@Before
public void setUp() throws Exception {
SecurityContextHolder.clearContext();
}
@After
public void tearDown() throws Exception {
SecurityContextHolder.clearContext();
}
@After
public void tearDown() throws Exception {
SecurityContextHolder.clearContext();
}
@Test
public void testSecurityCheckNoACEs() throws Exception {
Authentication auth = new TestingAuthenticationToken("user", "password","ROLE_GENERAL","ROLE_AUDITING","ROLE_OWNERSHIP");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
@Test
public void testSecurityCheckNoACEs() throws Exception {
Authentication auth = new TestingAuthenticationToken("user", "password",
"ROLE_GENERAL", "ROLE_AUDITING", "ROLE_OWNERSHIP");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority(
"ROLE_AUDITING"), new SimpleGrantedAuthority("ROLE_GENERAL"));
Acl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
Acl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy,
new ConsoleAuditLogger());
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_GENERAL);
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_AUDITING);
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
aclAuthorizationStrategy.securityCheck(acl,
AclAuthorizationStrategy.CHANGE_GENERAL);
aclAuthorizationStrategy.securityCheck(acl,
AclAuthorizationStrategy.CHANGE_AUDITING);
aclAuthorizationStrategy.securityCheck(acl,
AclAuthorizationStrategy.CHANGE_OWNERSHIP);
// Create another authorization strategy
AclAuthorizationStrategy aclAuthorizationStrategy2 = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_ONE"), new SimpleGrantedAuthority("ROLE_TWO"),
new SimpleGrantedAuthority("ROLE_THREE"));
Acl acl2 = new AclImpl(identity, new Long(1), aclAuthorizationStrategy2, new ConsoleAuditLogger());
// Check access in case the principal has no authorization rights
try {
aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_GENERAL);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
try {
aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_AUDITING);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
try {
aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
}
// Create another authorization strategy
AclAuthorizationStrategy aclAuthorizationStrategy2 = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_ONE"), new SimpleGrantedAuthority(
"ROLE_TWO"), new SimpleGrantedAuthority("ROLE_THREE"));
Acl acl2 = new AclImpl(identity, new Long(1), aclAuthorizationStrategy2,
new ConsoleAuditLogger());
// Check access in case the principal has no authorization rights
try {
aclAuthorizationStrategy2.securityCheck(acl2,
AclAuthorizationStrategy.CHANGE_GENERAL);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
try {
aclAuthorizationStrategy2.securityCheck(acl2,
AclAuthorizationStrategy.CHANGE_AUDITING);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
try {
aclAuthorizationStrategy2.securityCheck(acl2,
AclAuthorizationStrategy.CHANGE_OWNERSHIP);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
}
@Test
public void testSecurityCheckWithMultipleACEs() throws Exception {
// Create a simple authentication with ROLE_GENERAL
Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_GENERAL");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
@Test
public void testSecurityCheckWithMultipleACEs() throws Exception {
// Create a simple authentication with ROLE_GENERAL
Authentication auth = new TestingAuthenticationToken("user", "password",
"ROLE_GENERAL");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
// Authorization strategy will require a different role for each access
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
// Authorization strategy will require a different role for each access
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority(
"ROLE_AUDITING"), new SimpleGrantedAuthority("ROLE_GENERAL"));
// Let's give the principal the ADMINISTRATION permission, without
// granting access
MutableAcl aclFirstDeny = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
aclFirstDeny.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), false);
// Let's give the principal the ADMINISTRATION permission, without
// granting access
MutableAcl aclFirstDeny = new AclImpl(identity, new Long(1),
aclAuthorizationStrategy, new ConsoleAuditLogger());
aclFirstDeny.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth),
false);
// The CHANGE_GENERAL test should pass as the principal has ROLE_GENERAL
aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_GENERAL);
// The CHANGE_GENERAL test should pass as the principal has ROLE_GENERAL
aclAuthorizationStrategy.securityCheck(aclFirstDeny,
AclAuthorizationStrategy.CHANGE_GENERAL);
// The CHANGE_AUDITING and CHANGE_OWNERSHIP should fail since the
// principal doesn't have these authorities,
// nor granting access
try {
aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_AUDITING);
fail("It should have thrown AccessDeniedException");
}
catch (AccessDeniedException expected) {
}
try {
aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
fail("It should have thrown AccessDeniedException");
}
catch (AccessDeniedException expected) {
}
// The CHANGE_AUDITING and CHANGE_OWNERSHIP should fail since the
// principal doesn't have these authorities,
// nor granting access
try {
aclAuthorizationStrategy.securityCheck(aclFirstDeny,
AclAuthorizationStrategy.CHANGE_AUDITING);
fail("It should have thrown AccessDeniedException");
}
catch (AccessDeniedException expected) {
}
try {
aclAuthorizationStrategy.securityCheck(aclFirstDeny,
AclAuthorizationStrategy.CHANGE_OWNERSHIP);
fail("It should have thrown AccessDeniedException");
}
catch (AccessDeniedException expected) {
}
// Add granting access to this principal
aclFirstDeny.insertAce(1, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
// and try again for CHANGE_AUDITING - the first ACE's granting flag
// (false) will deny this access
try {
aclAuthorizationStrategy.securityCheck(aclFirstDeny, AclAuthorizationStrategy.CHANGE_AUDITING);
fail("It should have thrown AccessDeniedException");
}
catch (AccessDeniedException expected) {
}
// Add granting access to this principal
aclFirstDeny.insertAce(1, BasePermission.ADMINISTRATION, new PrincipalSid(auth),
true);
// and try again for CHANGE_AUDITING - the first ACE's granting flag
// (false) will deny this access
try {
aclAuthorizationStrategy.securityCheck(aclFirstDeny,
AclAuthorizationStrategy.CHANGE_AUDITING);
fail("It should have thrown AccessDeniedException");
}
catch (AccessDeniedException expected) {
}
// Create another ACL and give the principal the ADMINISTRATION
// permission, with granting access
MutableAcl aclFirstAllow = new AclImpl(identity, new Long(1), aclAuthorizationStrategy,
new ConsoleAuditLogger());
aclFirstAllow.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
// Create another ACL and give the principal the ADMINISTRATION
// permission, with granting access
MutableAcl aclFirstAllow = new AclImpl(identity, new Long(1),
aclAuthorizationStrategy, new ConsoleAuditLogger());
aclFirstAllow.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth),
true);
// The CHANGE_AUDITING test should pass as there is one ACE with
// granting access
// The CHANGE_AUDITING test should pass as there is one ACE with
// granting access
aclAuthorizationStrategy.securityCheck(aclFirstAllow, AclAuthorizationStrategy.CHANGE_AUDITING);
aclAuthorizationStrategy.securityCheck(aclFirstAllow,
AclAuthorizationStrategy.CHANGE_AUDITING);
// Add a deny ACE and test again for CHANGE_AUDITING
aclFirstAllow.insertAce(1, BasePermission.ADMINISTRATION, new PrincipalSid(auth), false);
try {
aclAuthorizationStrategy.securityCheck(aclFirstAllow, AclAuthorizationStrategy.CHANGE_AUDITING);
assertTrue(true);
}
catch (AccessDeniedException notExpected) {
fail("It shouldn't have thrown AccessDeniedException");
}
// Add a deny ACE and test again for CHANGE_AUDITING
aclFirstAllow.insertAce(1, BasePermission.ADMINISTRATION, new PrincipalSid(auth),
false);
try {
aclAuthorizationStrategy.securityCheck(aclFirstAllow,
AclAuthorizationStrategy.CHANGE_AUDITING);
assertTrue(true);
}
catch (AccessDeniedException notExpected) {
fail("It shouldn't have thrown AccessDeniedException");
}
// Create an ACL with no ACE
MutableAcl aclNoACE = new AclImpl(identity, new Long(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
try {
aclAuthorizationStrategy.securityCheck(aclNoACE, AclAuthorizationStrategy.CHANGE_AUDITING);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
assertTrue(true);
}
// and still grant access for CHANGE_GENERAL
try {
aclAuthorizationStrategy.securityCheck(aclNoACE, AclAuthorizationStrategy.CHANGE_GENERAL);
assertTrue(true);
}
catch (NotFoundException expected) {
fail("It shouldn't have thrown NotFoundException");
}
}
// Create an ACL with no ACE
MutableAcl aclNoACE = new AclImpl(identity, new Long(1),
aclAuthorizationStrategy, new ConsoleAuditLogger());
try {
aclAuthorizationStrategy.securityCheck(aclNoACE,
AclAuthorizationStrategy.CHANGE_AUDITING);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
assertTrue(true);
}
// and still grant access for CHANGE_GENERAL
try {
aclAuthorizationStrategy.securityCheck(aclNoACE,
AclAuthorizationStrategy.CHANGE_GENERAL);
assertTrue(true);
}
catch (NotFoundException expected) {
fail("It shouldn't have thrown NotFoundException");
}
}
@Test
public void testSecurityCheckWithInheritableACEs() throws Exception {
// Create a simple authentication with ROLE_GENERAL
Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_GENERAL");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
@Test
public void testSecurityCheckWithInheritableACEs() throws Exception {
// Create a simple authentication with ROLE_GENERAL
Authentication auth = new TestingAuthenticationToken("user", "password",
"ROLE_GENERAL");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, 100);
// Authorization strategy will require a different role for each access
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_ONE"), new SimpleGrantedAuthority("ROLE_TWO"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, 100);
// Authorization strategy will require a different role for each access
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_ONE"), new SimpleGrantedAuthority(
"ROLE_TWO"), new SimpleGrantedAuthority("ROLE_GENERAL"));
// Let's give the principal an ADMINISTRATION permission, with granting
// access
MutableAcl parentAcl = new AclImpl(identity, 1, aclAuthorizationStrategy, new ConsoleAuditLogger());
parentAcl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
MutableAcl childAcl = new AclImpl(identity, 2, aclAuthorizationStrategy, new ConsoleAuditLogger());
// Let's give the principal an ADMINISTRATION permission, with granting
// access
MutableAcl parentAcl = new AclImpl(identity, 1, aclAuthorizationStrategy,
new ConsoleAuditLogger());
parentAcl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth),
true);
MutableAcl childAcl = new AclImpl(identity, 2, aclAuthorizationStrategy,
new ConsoleAuditLogger());
// Check against the 'child' acl, which doesn't offer any authorization
// rights on CHANGE_OWNERSHIP
try {
aclAuthorizationStrategy.securityCheck(childAcl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
assertTrue(true);
}
// Check against the 'child' acl, which doesn't offer any authorization
// rights on CHANGE_OWNERSHIP
try {
aclAuthorizationStrategy.securityCheck(childAcl,
AclAuthorizationStrategy.CHANGE_OWNERSHIP);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
assertTrue(true);
}
// Link the child with its parent and test again against the
// CHANGE_OWNERSHIP right
childAcl.setParent(parentAcl);
childAcl.setEntriesInheriting(true);
try {
aclAuthorizationStrategy.securityCheck(childAcl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
assertTrue(true);
}
catch (NotFoundException expected) {
fail("It shouldn't have thrown NotFoundException");
}
// Link the child with its parent and test again against the
// CHANGE_OWNERSHIP right
childAcl.setParent(parentAcl);
childAcl.setEntriesInheriting(true);
try {
aclAuthorizationStrategy.securityCheck(childAcl,
AclAuthorizationStrategy.CHANGE_OWNERSHIP);
assertTrue(true);
}
catch (NotFoundException expected) {
fail("It shouldn't have thrown NotFoundException");
}
// Create a root parent and link it to the middle parent
MutableAcl rootParentAcl = new AclImpl(identity, 1, aclAuthorizationStrategy,
new ConsoleAuditLogger());
parentAcl = new AclImpl(identity, 1, aclAuthorizationStrategy, new ConsoleAuditLogger());
rootParentAcl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth), true);
parentAcl.setEntriesInheriting(true);
parentAcl.setParent(rootParentAcl);
childAcl.setParent(parentAcl);
try {
aclAuthorizationStrategy.securityCheck(childAcl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
assertTrue(true);
}
catch (NotFoundException expected) {
fail("It shouldn't have thrown NotFoundException");
}
}
// Create a root parent and link it to the middle parent
MutableAcl rootParentAcl = new AclImpl(identity, 1, aclAuthorizationStrategy,
new ConsoleAuditLogger());
parentAcl = new AclImpl(identity, 1, aclAuthorizationStrategy,
new ConsoleAuditLogger());
rootParentAcl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth),
true);
parentAcl.setEntriesInheriting(true);
parentAcl.setParent(rootParentAcl);
childAcl.setParent(parentAcl);
try {
aclAuthorizationStrategy.securityCheck(childAcl,
AclAuthorizationStrategy.CHANGE_OWNERSHIP);
assertTrue(true);
}
catch (NotFoundException expected) {
fail("It shouldn't have thrown NotFoundException");
}
}
@Test
public void testSecurityCheckPrincipalOwner() throws Exception {
Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_ONE");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
@Test
public void testSecurityCheckPrincipalOwner() throws Exception {
Authentication auth = new TestingAuthenticationToken("user", "password",
"ROLE_ONE");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, 100);
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, 100);
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority(
"ROLE_AUDITING"), new SimpleGrantedAuthority("ROLE_GENERAL"));
Acl acl = new AclImpl(identity, 1, aclAuthorizationStrategy, new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()), null, null,
false, new PrincipalSid(auth));
try {
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_GENERAL);
}
catch (AccessDeniedException notExpected) {
fail("It shouldn't have thrown AccessDeniedException");
}
try {
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_AUDITING);
fail("It shouldn't have thrown AccessDeniedException");
}
catch (NotFoundException expected) {
}
try {
aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_OWNERSHIP);
}
catch (AccessDeniedException notExpected) {
fail("It shouldn't have thrown AccessDeniedException");
}
}
Acl acl = new AclImpl(identity, 1, aclAuthorizationStrategy,
new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()), null,
null, false, new PrincipalSid(auth));
try {
aclAuthorizationStrategy.securityCheck(acl,
AclAuthorizationStrategy.CHANGE_GENERAL);
}
catch (AccessDeniedException notExpected) {
fail("It shouldn't have thrown AccessDeniedException");
}
try {
aclAuthorizationStrategy.securityCheck(acl,
AclAuthorizationStrategy.CHANGE_AUDITING);
fail("It shouldn't have thrown AccessDeniedException");
}
catch (NotFoundException expected) {
}
try {
aclAuthorizationStrategy.securityCheck(acl,
AclAuthorizationStrategy.CHANGE_OWNERSHIP);
}
catch (AccessDeniedException notExpected) {
fail("It shouldn't have thrown AccessDeniedException");
}
}
}

View File

@ -18,61 +18,63 @@ import org.springframework.security.acls.model.AuditableAccessControlEntry;
* @author Andrei Stefan
*/
public class AuditLoggerTests {
//~ Instance fields ================================================================================================
private PrintStream console;
private ByteArrayOutputStream bytes = new ByteArrayOutputStream();
private ConsoleAuditLogger logger;
private AuditableAccessControlEntry ace;
// ~ Instance fields
// ================================================================================================
private PrintStream console;
private ByteArrayOutputStream bytes = new ByteArrayOutputStream();
private ConsoleAuditLogger logger;
private AuditableAccessControlEntry ace;
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
@Before
public void setUp() throws Exception {
logger = new ConsoleAuditLogger();
ace = mock(AuditableAccessControlEntry.class);
console = System.out;
System.setOut(new PrintStream(bytes));
}
@Before
public void setUp() throws Exception {
logger = new ConsoleAuditLogger();
ace = mock(AuditableAccessControlEntry.class);
console = System.out;
System.setOut(new PrintStream(bytes));
}
@After
public void tearDown() throws Exception {
System.setOut(console);
bytes.reset();
}
@After
public void tearDown() throws Exception {
System.setOut(console);
bytes.reset();
}
@Test
public void nonAuditableAceIsIgnored() {
AccessControlEntry ace = mock(AccessControlEntry.class);
logger.logIfNeeded(true, ace);
assertEquals(0, bytes.size());
}
@Test
public void nonAuditableAceIsIgnored() {
AccessControlEntry ace = mock(AccessControlEntry.class);
logger.logIfNeeded(true, ace);
assertEquals(0, bytes.size());
}
@Test
public void successIsNotLoggedIfAceDoesntRequireSuccessAudit() throws Exception {
when(ace.isAuditSuccess()).thenReturn(false);
logger.logIfNeeded(true, ace);
assertEquals(0, bytes.size());
}
@Test
public void successIsNotLoggedIfAceDoesntRequireSuccessAudit() throws Exception {
when(ace.isAuditSuccess()).thenReturn(false);
logger.logIfNeeded(true, ace);
assertEquals(0, bytes.size());
}
@Test
public void successIsLoggedIfAceRequiresSuccessAudit() throws Exception {
when(ace.isAuditSuccess()).thenReturn(true);
@Test
public void successIsLoggedIfAceRequiresSuccessAudit() throws Exception {
when(ace.isAuditSuccess()).thenReturn(true);
logger.logIfNeeded(true, ace);
assertTrue(bytes.toString().startsWith("GRANTED due to ACE"));
}
logger.logIfNeeded(true, ace);
assertTrue(bytes.toString().startsWith("GRANTED due to ACE"));
}
@Test
public void failureIsntLoggedIfAceDoesntRequireFailureAudit() throws Exception {
when(ace.isAuditFailure()).thenReturn(false);
logger.logIfNeeded(false, ace);
assertEquals(0, bytes.size());
}
@Test
public void failureIsntLoggedIfAceDoesntRequireFailureAudit() throws Exception {
when(ace.isAuditFailure()).thenReturn(false);
logger.logIfNeeded(false, ace);
assertEquals(0, bytes.size());
}
@Test
public void failureIsLoggedIfAceRequiresFailureAudit() throws Exception {
when(ace.isAuditFailure()).thenReturn(true);
logger.logIfNeeded(false, ace);
assertTrue(bytes.toString().startsWith("DENIED due to ACE"));
}
@Test
public void failureIsLoggedIfAceRequiresFailureAudit() throws Exception {
when(ace.isAuditFailure()).thenReturn(true);
logger.logIfNeeded(false, ace);
assertTrue(bytes.toString().startsWith("DENIED due to ACE"));
}
}

View File

@ -15,175 +15,179 @@ import org.springframework.security.acls.model.ObjectIdentity;
@SuppressWarnings("unused")
public class ObjectIdentityImplTests {
private static final String DOMAIN_CLASS =
"org.springframework.security.acls.domain.ObjectIdentityImplTests$MockIdDomainObject";
private static final String DOMAIN_CLASS = "org.springframework.security.acls.domain.ObjectIdentityImplTests$MockIdDomainObject";
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
@Test
public void constructorsRespectRequiredFields() throws Exception {
// Check one-argument constructor required field
try {
new ObjectIdentityImpl(null);
fail("It should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
@Test
public void constructorsRespectRequiredFields() throws Exception {
// Check one-argument constructor required field
try {
new ObjectIdentityImpl(null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// Check String-Serializable constructor required field
try {
new ObjectIdentityImpl("", Long.valueOf(1));
fail("It should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
// Check String-Serializable constructor required field
try {
new ObjectIdentityImpl("", Long.valueOf(1));
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// Check Serializable parameter is not null
try {
new ObjectIdentityImpl(DOMAIN_CLASS, null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// Check Serializable parameter is not null
try {
new ObjectIdentityImpl(DOMAIN_CLASS, null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// The correct way of using String-Serializable constructor
try {
new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(1));
}
catch (IllegalArgumentException notExpected) {
fail("It shouldn't have thrown IllegalArgumentException");
}
// The correct way of using String-Serializable constructor
try {
new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(1));
}
catch (IllegalArgumentException notExpected) {
fail("It shouldn't have thrown IllegalArgumentException");
}
// Check the Class-Serializable constructor
try {
new ObjectIdentityImpl(MockIdDomainObject.class, null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
// Check the Class-Serializable constructor
try {
new ObjectIdentityImpl(MockIdDomainObject.class, null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
@Test
public void gettersReturnExpectedValues() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(1));
assertEquals(Long.valueOf(1), obj.getIdentifier());
assertEquals(MockIdDomainObject.class.getName(), obj.getType());
}
@Test
public void gettersReturnExpectedValues() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(1));
assertEquals(Long.valueOf(1), obj.getIdentifier());
assertEquals(MockIdDomainObject.class.getName(), obj.getType());
}
@Test
public void testGetIdMethodConstraints() throws Exception {
// Check the getId() method is present
try {
new ObjectIdentityImpl("A_STRING_OBJECT");
fail("It should have thrown IdentityUnavailableException");
}
catch (IdentityUnavailableException expected) {
@Test
public void testGetIdMethodConstraints() throws Exception {
// Check the getId() method is present
try {
new ObjectIdentityImpl("A_STRING_OBJECT");
fail("It should have thrown IdentityUnavailableException");
}
catch (IdentityUnavailableException expected) {
}
}
// getId() should return a non-null value
MockIdDomainObject mockId = new MockIdDomainObject();
try {
new ObjectIdentityImpl(mockId);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
// getId() should return a non-null value
MockIdDomainObject mockId = new MockIdDomainObject();
try {
new ObjectIdentityImpl(mockId);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
// getId() should return a Serializable object
mockId.setId(new MockIdDomainObject());
try {
new ObjectIdentityImpl(mockId);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// getId() should return a Serializable object
mockId.setId(new MockIdDomainObject());
try {
new ObjectIdentityImpl(mockId);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// getId() should return a Serializable object
mockId.setId(new Long(100));
try {
new ObjectIdentityImpl(mockId);
}
catch (IllegalArgumentException expected) {
}
}
// getId() should return a Serializable object
mockId.setId(new Long(100));
try {
new ObjectIdentityImpl(mockId);
}
catch (IllegalArgumentException expected) {
}
}
@Test(expected=IllegalArgumentException.class)
public void constructorRejectsInvalidTypeParameter() throws Exception {
new ObjectIdentityImpl("", Long.valueOf(1));
}
@Test(expected = IllegalArgumentException.class)
public void constructorRejectsInvalidTypeParameter() throws Exception {
new ObjectIdentityImpl("", Long.valueOf(1));
}
@Test
public void testEquals() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(1));
MockIdDomainObject mockObj = new MockIdDomainObject();
mockObj.setId(Long.valueOf(1));
@Test
public void testEquals() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(1));
MockIdDomainObject mockObj = new MockIdDomainObject();
mockObj.setId(Long.valueOf(1));
String string = "SOME_STRING";
assertNotSame(obj, string);
assertFalse(obj.equals(null));
assertFalse(obj.equals("DIFFERENT_OBJECT_TYPE"));
assertFalse(obj.equals(new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(2))));
assertFalse(obj.equals(new ObjectIdentityImpl(
"org.springframework.security.acls.domain.ObjectIdentityImplTests$MockOtherIdDomainObject",
Long.valueOf(1))));
assertEquals(new ObjectIdentityImpl(DOMAIN_CLASS,Long.valueOf(1)), obj);
assertEquals(obj, new ObjectIdentityImpl(mockObj));
}
String string = "SOME_STRING";
assertNotSame(obj, string);
assertFalse(obj.equals(null));
assertFalse(obj.equals("DIFFERENT_OBJECT_TYPE"));
assertFalse(obj.equals(new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(2))));
assertFalse(obj
.equals(new ObjectIdentityImpl(
"org.springframework.security.acls.domain.ObjectIdentityImplTests$MockOtherIdDomainObject",
Long.valueOf(1))));
assertEquals(new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(1)), obj);
assertEquals(obj, new ObjectIdentityImpl(mockObj));
}
@Test
public void hashcodeIsDifferentForDifferentJavaTypes() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, Long.valueOf(1));
ObjectIdentity obj2 = new ObjectIdentityImpl(String.class, Long.valueOf(1));
assertFalse(obj.hashCode() == obj2.hashCode());
}
@Test
public void hashcodeIsDifferentForDifferentJavaTypes() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, Long.valueOf(1));
ObjectIdentity obj2 = new ObjectIdentityImpl(String.class, Long.valueOf(1));
assertFalse(obj.hashCode() == obj2.hashCode());
}
@Test
public void longAndIntegerIdsWithSameValueAreEqualAndHaveSameHashcode() {
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, new Long(5));
ObjectIdentity obj2 = new ObjectIdentityImpl(Object.class, Integer.valueOf(5));
@Test
public void longAndIntegerIdsWithSameValueAreEqualAndHaveSameHashcode() {
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, new Long(5));
ObjectIdentity obj2 = new ObjectIdentityImpl(Object.class, Integer.valueOf(5));
assertEquals(obj, obj2);
assertEquals(obj.hashCode(), obj2.hashCode());
}
assertEquals(obj, obj2);
assertEquals(obj.hashCode(), obj2.hashCode());
}
@Test
public void equalStringIdsAreEqualAndHaveSameHashcode() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, "1000");
ObjectIdentity obj2 = new ObjectIdentityImpl(Object.class, "1000");
assertEquals(obj, obj2);
assertEquals(obj.hashCode(), obj2.hashCode());
}
@Test
public void equalStringIdsAreEqualAndHaveSameHashcode() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, "1000");
ObjectIdentity obj2 = new ObjectIdentityImpl(Object.class, "1000");
assertEquals(obj, obj2);
assertEquals(obj.hashCode(), obj2.hashCode());
}
@Test
public void stringAndNumericIdsAreNotEqual() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, "1000");
ObjectIdentity obj2 = new ObjectIdentityImpl(Object.class, Long.valueOf(1000));
assertFalse(obj.equals(obj2));
}
@Test
public void stringAndNumericIdsAreNotEqual() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, "1000");
ObjectIdentity obj2 = new ObjectIdentityImpl(Object.class, Long.valueOf(1000));
assertFalse(obj.equals(obj2));
}
//~ Inner Classes ==================================================================================================
// ~ Inner Classes
// ==================================================================================================
private class MockIdDomainObject {
private Object id;
private class MockIdDomainObject {
private Object id;
public Object getId() {
return id;
}
public Object getId() {
return id;
}
public void setId(Object id) {
this.id = id;
}
}
public void setId(Object id) {
this.id = id;
}
}
private class MockOtherIdDomainObject {
private Object id;
private class MockOtherIdDomainObject {
private Object id;
public Object getId() {
return id;
}
public Object getId() {
return id;
}
public void setId(Object id) {
this.id = id;
}
}
public void setId(Object id) {
this.id = id;
}
}
}

View File

@ -13,30 +13,32 @@ import junit.framework.TestCase;
* @author Andrei Stefan
*/
public class ObjectIdentityRetrievalStrategyImplTests extends TestCase {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public void testObjectIdentityCreation() throws Exception {
MockIdDomainObject domain = new MockIdDomainObject();
domain.setId(Integer.valueOf(1));
public void testObjectIdentityCreation() throws Exception {
MockIdDomainObject domain = new MockIdDomainObject();
domain.setId(Integer.valueOf(1));
ObjectIdentityRetrievalStrategy retStrategy = new ObjectIdentityRetrievalStrategyImpl();
ObjectIdentity identity = retStrategy.getObjectIdentity(domain);
ObjectIdentityRetrievalStrategy retStrategy = new ObjectIdentityRetrievalStrategyImpl();
ObjectIdentity identity = retStrategy.getObjectIdentity(domain);
assertNotNull(identity);
assertEquals(identity, new ObjectIdentityImpl(domain));
}
assertNotNull(identity);
assertEquals(identity, new ObjectIdentityImpl(domain));
}
//~ Inner Classes ==================================================================================================
@SuppressWarnings("unused")
private class MockIdDomainObject {
private Object id;
// ~ Inner Classes
// ==================================================================================================
@SuppressWarnings("unused")
private class MockIdDomainObject {
private Object id;
public Object getId() {
return id;
}
public Object getId() {
return id;
}
public void setId(Object id) {
this.id = id;
}
}
public void setId(Object id) {
this.id = id;
}
}
}

View File

@ -20,7 +20,6 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.security.acls.model.Permission;
/**
* Tests classes associated with Permission.
*
@ -28,73 +27,92 @@ import org.springframework.security.acls.model.Permission;
*/
public class PermissionTests {
private DefaultPermissionFactory permissionFactory;
private DefaultPermissionFactory permissionFactory;
@Before
public void createPermissionfactory() {
permissionFactory = new DefaultPermissionFactory();
}
@Before
public void createPermissionfactory() {
permissionFactory = new DefaultPermissionFactory();
}
@Test
public void basePermissionTest() {
Permission p = permissionFactory.buildFromName("WRITE");
assertNotNull(p);
}
@Test
public void basePermissionTest() {
Permission p = permissionFactory.buildFromName("WRITE");
assertNotNull(p);
}
@Test
public void expectedIntegerValues() {
assertEquals(1, BasePermission.READ.getMask());
assertEquals(16, BasePermission.ADMINISTRATION.getMask());
assertEquals(7,
new CumulativePermission().set(BasePermission.READ).set(BasePermission.WRITE).set(BasePermission.CREATE)
.getMask());
assertEquals(17,
new CumulativePermission().set(BasePermission.READ).set(BasePermission.ADMINISTRATION).getMask());
}
@Test
public void expectedIntegerValues() {
assertEquals(1, BasePermission.READ.getMask());
assertEquals(16, BasePermission.ADMINISTRATION.getMask());
assertEquals(
7,
new CumulativePermission().set(BasePermission.READ)
.set(BasePermission.WRITE).set(BasePermission.CREATE).getMask());
assertEquals(
17,
new CumulativePermission().set(BasePermission.READ)
.set(BasePermission.ADMINISTRATION).getMask());
}
@Test
public void fromInteger() {
Permission permission = permissionFactory.buildFromMask(7);
System.out.println("7 = " + permission.toString());
permission = permissionFactory.buildFromMask(4);
System.out.println("4 = " + permission.toString());
}
@Test
public void fromInteger() {
Permission permission = permissionFactory.buildFromMask(7);
System.out.println("7 = " + permission.toString());
permission = permissionFactory.buildFromMask(4);
System.out.println("4 = " + permission.toString());
}
@Test
public void stringConversion() {
permissionFactory.registerPublicPermissions(SpecialPermission.class);
@Test
public void stringConversion() {
permissionFactory.registerPublicPermissions(SpecialPermission.class);
System.out.println("R = " + BasePermission.READ.toString());
assertEquals("BasePermission[...............................R=1]", BasePermission.READ.toString());
System.out.println("R = " + BasePermission.READ.toString());
assertEquals("BasePermission[...............................R=1]",
BasePermission.READ.toString());
System.out.println("A = " + BasePermission.ADMINISTRATION.toString());
assertEquals("BasePermission[...........................A....=16]", BasePermission.ADMINISTRATION.toString());
System.out.println("A = " + BasePermission.ADMINISTRATION.toString());
assertEquals("BasePermission[...........................A....=16]",
BasePermission.ADMINISTRATION.toString());
System.out.println("R = " + new CumulativePermission().set(BasePermission.READ).toString());
assertEquals("CumulativePermission[...............................R=1]",
new CumulativePermission().set(BasePermission.READ).toString());
System.out.println("R = "
+ new CumulativePermission().set(BasePermission.READ).toString());
assertEquals("CumulativePermission[...............................R=1]",
new CumulativePermission().set(BasePermission.READ).toString());
System.out.println("A = " + new CumulativePermission().set(SpecialPermission.ENTER).set(BasePermission.ADMINISTRATION).toString());
assertEquals("CumulativePermission[..........................EA....=48]",
new CumulativePermission().set(SpecialPermission.ENTER).set(BasePermission.ADMINISTRATION).toString());
System.out.println("A = "
+ new CumulativePermission().set(SpecialPermission.ENTER)
.set(BasePermission.ADMINISTRATION).toString());
assertEquals(
"CumulativePermission[..........................EA....=48]",
new CumulativePermission().set(SpecialPermission.ENTER)
.set(BasePermission.ADMINISTRATION).toString());
System.out.println("RA = "
+ new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ).toString());
assertEquals("CumulativePermission[...........................A...R=17]",
new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ).toString());
System.out.println("RA = "
+ new CumulativePermission().set(BasePermission.ADMINISTRATION)
.set(BasePermission.READ).toString());
assertEquals(
"CumulativePermission[...........................A...R=17]",
new CumulativePermission().set(BasePermission.ADMINISTRATION)
.set(BasePermission.READ).toString());
System.out.println("R = "
+ new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ)
.clear(BasePermission.ADMINISTRATION).toString());
assertEquals("CumulativePermission[...............................R=1]",
new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ)
.clear(BasePermission.ADMINISTRATION).toString());
System.out.println("R = "
+ new CumulativePermission().set(BasePermission.ADMINISTRATION)
.set(BasePermission.READ).clear(BasePermission.ADMINISTRATION)
.toString());
assertEquals(
"CumulativePermission[...............................R=1]",
new CumulativePermission().set(BasePermission.ADMINISTRATION)
.set(BasePermission.READ).clear(BasePermission.ADMINISTRATION)
.toString());
System.out.println("0 = "
+ new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ)
.clear(BasePermission.ADMINISTRATION).clear(BasePermission.READ).toString());
assertEquals("CumulativePermission[................................=0]",
new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ)
.clear(BasePermission.ADMINISTRATION).clear(BasePermission.READ).toString());
}
System.out.println("0 = "
+ new CumulativePermission().set(BasePermission.ADMINISTRATION)
.set(BasePermission.READ).clear(BasePermission.ADMINISTRATION)
.clear(BasePermission.READ).toString());
assertEquals(
"CumulativePermission[................................=0]",
new CumulativePermission().set(BasePermission.ADMINISTRATION)
.set(BasePermission.READ).clear(BasePermission.ADMINISTRATION)
.clear(BasePermission.READ).toString());
}
}

View File

@ -16,17 +16,16 @@ package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Permission;
/**
* A test permission.
*
* @author Ben Alex
*/
public class SpecialPermission extends BasePermission {
public static final Permission ENTER = new SpecialPermission(1 << 5, 'E'); // 32
public static final Permission LEAVE = new SpecialPermission(1 << 6, 'L');
public static final Permission ENTER = new SpecialPermission(1 << 5, 'E'); // 32
public static final Permission LEAVE = new SpecialPermission(1 << 6, 'L');
protected SpecialPermission(int mask, char code) {
super(mask, code);
}
protected SpecialPermission(int mask, char code) {
super(mask, code);
}
}

View File

@ -29,284 +29,335 @@ import java.util.*;
*/
public class BasicLookupStrategyTests {
private static final Sid BEN_SID = new PrincipalSid("ben");
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
private static final Sid BEN_SID = new PrincipalSid("ben");
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private static JdbcTemplate jdbcTemplate;
private BasicLookupStrategy strategy;
private static SingleConnectionDataSource dataSource;
private static CacheManager cacheManager;
private static JdbcTemplate jdbcTemplate;
private BasicLookupStrategy strategy;
private static SingleConnectionDataSource dataSource;
private static CacheManager cacheManager;
//~ Methods ========================================================================================================
@BeforeClass
public static void initCacheManaer() {
cacheManager = CacheManager.create();
cacheManager.addCache(new Cache("basiclookuptestcache", 500, false, false, 30, 30));
}
// ~ Methods
// ========================================================================================================
@BeforeClass
public static void initCacheManaer() {
cacheManager = CacheManager.create();
cacheManager
.addCache(new Cache("basiclookuptestcache", 500, false, false, 30, 30));
}
@BeforeClass
public static void createDatabase() throws Exception {
dataSource = new SingleConnectionDataSource("jdbc:hsqldb:mem:lookupstrategytest", "sa", "", true);
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
jdbcTemplate = new JdbcTemplate(dataSource);
@BeforeClass
public static void createDatabase() throws Exception {
dataSource = new SingleConnectionDataSource("jdbc:hsqldb:mem:lookupstrategytest",
"sa", "", true);
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
jdbcTemplate = new JdbcTemplate(dataSource);
Resource resource = new ClassPathResource("createAclSchema.sql");
String sql = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
jdbcTemplate.execute(sql);
}
Resource resource = new ClassPathResource("createAclSchema.sql");
String sql = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
jdbcTemplate.execute(sql);
}
@AfterClass
public static void dropDatabase() throws Exception {
dataSource.destroy();
}
@AfterClass
public static void dropDatabase() throws Exception {
dataSource.destroy();
}
@AfterClass
public static void shutdownCacheManager() {
cacheManager.removalAll();
cacheManager.shutdown();
}
@AfterClass
public static void shutdownCacheManager() {
cacheManager.removalAll();
cacheManager.shutdown();
}
@Before
public void populateDatabase() {
String query = "INSERT INTO acl_sid(ID,PRINCIPAL,SID) VALUES (1,1,'ben');"
+ "INSERT INTO acl_class(ID,CLASS) VALUES (2,'" + TARGET_CLASS + "');"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (1,2,100,null,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (2,2,101,1,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (3,2,102,2,1,1);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (1,1,0,1,1,1,0,0);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (2,1,1,1,2,0,0,0);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (3,2,0,1,8,1,0,0);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (4,3,0,1,8,0,0,0);";
jdbcTemplate.execute(query);
}
@Before
public void populateDatabase() {
String query = "INSERT INTO acl_sid(ID,PRINCIPAL,SID) VALUES (1,1,'ben');"
+ "INSERT INTO acl_class(ID,CLASS) VALUES (2,'"
+ TARGET_CLASS
+ "');"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (1,2,100,null,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (2,2,101,1,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (3,2,102,2,1,1);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (1,1,0,1,1,1,0,0);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (2,1,1,1,2,0,0,0);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (3,2,0,1,8,1,0,0);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (4,3,0,1,8,0,0,0);";
jdbcTemplate.execute(query);
}
@Before
public void initializeBeans() {
EhCacheBasedAclCache cache = new EhCacheBasedAclCache(getCache(), new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()), new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_USER")));
AclAuthorizationStrategy authorizationStrategy = new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_ADMINISTRATOR"));
strategy = new BasicLookupStrategy(dataSource, cache, authorizationStrategy,
new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()));
strategy.setPermissionFactory(new DefaultPermissionFactory());
}
@Before
public void initializeBeans() {
EhCacheBasedAclCache cache = new EhCacheBasedAclCache(getCache(),
new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()),
new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_USER")));
AclAuthorizationStrategy authorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_ADMINISTRATOR"));
strategy = new BasicLookupStrategy(dataSource, cache, authorizationStrategy,
new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()));
strategy.setPermissionFactory(new DefaultPermissionFactory());
}
@After
public void emptyDatabase() {
String query = "DELETE FROM acl_entry;" + "DELETE FROM acl_object_identity WHERE ID = 7;"
+ "DELETE FROM acl_object_identity WHERE ID = 6;" + "DELETE FROM acl_object_identity WHERE ID = 5;"
+ "DELETE FROM acl_object_identity WHERE ID = 4;" + "DELETE FROM acl_object_identity WHERE ID = 3;"
+ "DELETE FROM acl_object_identity WHERE ID = 2;" + "DELETE FROM acl_object_identity WHERE ID = 1;"
+ "DELETE FROM acl_class;" + "DELETE FROM acl_sid;";
jdbcTemplate.execute(query);
}
@After
public void emptyDatabase() {
String query = "DELETE FROM acl_entry;"
+ "DELETE FROM acl_object_identity WHERE ID = 7;"
+ "DELETE FROM acl_object_identity WHERE ID = 6;"
+ "DELETE FROM acl_object_identity WHERE ID = 5;"
+ "DELETE FROM acl_object_identity WHERE ID = 4;"
+ "DELETE FROM acl_object_identity WHERE ID = 3;"
+ "DELETE FROM acl_object_identity WHERE ID = 2;"
+ "DELETE FROM acl_object_identity WHERE ID = 1;"
+ "DELETE FROM acl_class;" + "DELETE FROM acl_sid;";
jdbcTemplate.execute(query);
}
private Ehcache getCache() {
Ehcache cache = cacheManager.getCache("basiclookuptestcache");
cache.removeAll();
return cache;
}
private Ehcache getCache() {
Ehcache cache = cacheManager.getCache("basiclookuptestcache");
cache.removeAll();
return cache;
}
@Test
public void testAclsRetrievalWithDefaultBatchSize() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(101));
// Deliberately use an integer for the child, to reproduce bug report in SEC-819
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(102));
@Test
public void testAclsRetrievalWithDefaultBatchSize() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(
101));
// Deliberately use an integer for the child, to reproduce bug report in SEC-819
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS,
Integer.valueOf(102));
Map<ObjectIdentity, Acl> map = this.strategy.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
checkEntries(topParentOid, middleParentOid, childOid, map);
}
Map<ObjectIdentity, Acl> map = this.strategy.readAclsById(
Arrays.asList(topParentOid, middleParentOid, childOid), null);
checkEntries(topParentOid, middleParentOid, childOid, map);
}
@Test
public void testAclsRetrievalFromCacheOnly() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(101));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(102));
@Test
public void testAclsRetrievalFromCacheOnly() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS,
Integer.valueOf(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(
101));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(102));
// Objects were put in cache
strategy.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
// Objects were put in cache
strategy.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid),
null);
// Let's empty the database to force acls retrieval from cache
emptyDatabase();
Map<ObjectIdentity, Acl> map = this.strategy.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
// Let's empty the database to force acls retrieval from cache
emptyDatabase();
Map<ObjectIdentity, Acl> map = this.strategy.readAclsById(
Arrays.asList(topParentOid, middleParentOid, childOid), null);
checkEntries(topParentOid, middleParentOid, childOid, map);
}
checkEntries(topParentOid, middleParentOid, childOid, map);
}
@Test
public void testAclsRetrievalWithCustomBatchSize() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(101));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(102));
@Test
public void testAclsRetrievalWithCustomBatchSize() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS,
Integer.valueOf(101));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(102));
// Set a batch size to allow multiple database queries in order to retrieve all acls
this.strategy.setBatchSize(1);
Map<ObjectIdentity, Acl> map = this.strategy.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
checkEntries(topParentOid, middleParentOid, childOid, map);
}
// Set a batch size to allow multiple database queries in order to retrieve all
// acls
this.strategy.setBatchSize(1);
Map<ObjectIdentity, Acl> map = this.strategy.readAclsById(
Arrays.asList(topParentOid, middleParentOid, childOid), null);
checkEntries(topParentOid, middleParentOid, childOid, map);
}
private void checkEntries(ObjectIdentity topParentOid, ObjectIdentity middleParentOid, ObjectIdentity childOid,
Map<ObjectIdentity, Acl> map) throws Exception {
Assert.assertEquals(3, map.size());
private void checkEntries(ObjectIdentity topParentOid,
ObjectIdentity middleParentOid, ObjectIdentity childOid,
Map<ObjectIdentity, Acl> map) throws Exception {
Assert.assertEquals(3, map.size());
MutableAcl topParent = (MutableAcl) map.get(topParentOid);
MutableAcl middleParent = (MutableAcl) map.get(middleParentOid);
MutableAcl child = (MutableAcl) map.get(childOid);
MutableAcl topParent = (MutableAcl) map.get(topParentOid);
MutableAcl middleParent = (MutableAcl) map.get(middleParentOid);
MutableAcl child = (MutableAcl) map.get(childOid);
// Check the retrieved versions has IDs
Assert.assertNotNull(topParent.getId());
Assert.assertNotNull(middleParent.getId());
Assert.assertNotNull(child.getId());
// Check the retrieved versions has IDs
Assert.assertNotNull(topParent.getId());
Assert.assertNotNull(middleParent.getId());
Assert.assertNotNull(child.getId());
// Check their parents were correctly retrieved
Assert.assertNull(topParent.getParentAcl());
Assert.assertEquals(topParentOid, middleParent.getParentAcl().getObjectIdentity());
Assert.assertEquals(middleParentOid, child.getParentAcl().getObjectIdentity());
// Check their parents were correctly retrieved
Assert.assertNull(topParent.getParentAcl());
Assert.assertEquals(topParentOid, middleParent.getParentAcl().getObjectIdentity());
Assert.assertEquals(middleParentOid, child.getParentAcl().getObjectIdentity());
// Check their ACEs were correctly retrieved
Assert.assertEquals(2, topParent.getEntries().size());
Assert.assertEquals(1, middleParent.getEntries().size());
Assert.assertEquals(1, child.getEntries().size());
// Check their ACEs were correctly retrieved
Assert.assertEquals(2, topParent.getEntries().size());
Assert.assertEquals(1, middleParent.getEntries().size());
Assert.assertEquals(1, child.getEntries().size());
// Check object identities were correctly retrieved
Assert.assertEquals(topParentOid, topParent.getObjectIdentity());
Assert.assertEquals(middleParentOid, middleParent.getObjectIdentity());
Assert.assertEquals(childOid, child.getObjectIdentity());
// Check object identities were correctly retrieved
Assert.assertEquals(topParentOid, topParent.getObjectIdentity());
Assert.assertEquals(middleParentOid, middleParent.getObjectIdentity());
Assert.assertEquals(childOid, child.getObjectIdentity());
// Check each entry
Assert.assertTrue(topParent.isEntriesInheriting());
Assert.assertEquals(topParent.getId(), Long.valueOf(1));
Assert.assertEquals(topParent.getOwner(), new PrincipalSid("ben"));
Assert.assertEquals(topParent.getEntries().get(0).getId(), Long.valueOf(1));
Assert.assertEquals(topParent.getEntries().get(0).getPermission(), BasePermission.READ);
Assert.assertEquals(topParent.getEntries().get(0).getSid(), new PrincipalSid("ben"));
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries().get(0)).isAuditFailure());
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries().get(0)).isAuditSuccess());
Assert.assertTrue((topParent.getEntries().get(0)).isGranting());
// Check each entry
Assert.assertTrue(topParent.isEntriesInheriting());
Assert.assertEquals(topParent.getId(), Long.valueOf(1));
Assert.assertEquals(topParent.getOwner(), new PrincipalSid("ben"));
Assert.assertEquals(topParent.getEntries().get(0).getId(), Long.valueOf(1));
Assert.assertEquals(topParent.getEntries().get(0).getPermission(),
BasePermission.READ);
Assert.assertEquals(topParent.getEntries().get(0).getSid(), new PrincipalSid(
"ben"));
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries().get(0))
.isAuditFailure());
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries().get(0))
.isAuditSuccess());
Assert.assertTrue((topParent.getEntries().get(0)).isGranting());
Assert.assertEquals(topParent.getEntries().get(1).getId(), Long.valueOf(2));
Assert.assertEquals(topParent.getEntries().get(1).getPermission(), BasePermission.WRITE);
Assert.assertEquals(topParent.getEntries().get(1).getSid(), new PrincipalSid("ben"));
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries().get(1)).isAuditFailure());
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries().get(1)).isAuditSuccess());
Assert.assertFalse(topParent.getEntries().get(1).isGranting());
Assert.assertEquals(topParent.getEntries().get(1).getId(), Long.valueOf(2));
Assert.assertEquals(topParent.getEntries().get(1).getPermission(),
BasePermission.WRITE);
Assert.assertEquals(topParent.getEntries().get(1).getSid(), new PrincipalSid(
"ben"));
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries().get(1))
.isAuditFailure());
Assert.assertFalse(((AuditableAccessControlEntry) topParent.getEntries().get(1))
.isAuditSuccess());
Assert.assertFalse(topParent.getEntries().get(1).isGranting());
Assert.assertTrue(middleParent.isEntriesInheriting());
Assert.assertEquals(middleParent.getId(), Long.valueOf(2));
Assert.assertEquals(middleParent.getOwner(), new PrincipalSid("ben"));
Assert.assertEquals(middleParent.getEntries().get(0).getId(), Long.valueOf(3));
Assert.assertEquals(middleParent.getEntries().get(0).getPermission(), BasePermission.DELETE);
Assert.assertEquals(middleParent.getEntries().get(0).getSid(), new PrincipalSid("ben"));
Assert.assertFalse(((AuditableAccessControlEntry) middleParent.getEntries().get(0)).isAuditFailure());
Assert.assertFalse(((AuditableAccessControlEntry) middleParent.getEntries().get(0)).isAuditSuccess());
Assert.assertTrue(middleParent.getEntries().get(0).isGranting());
Assert.assertTrue(middleParent.isEntriesInheriting());
Assert.assertEquals(middleParent.getId(), Long.valueOf(2));
Assert.assertEquals(middleParent.getOwner(), new PrincipalSid("ben"));
Assert.assertEquals(middleParent.getEntries().get(0).getId(), Long.valueOf(3));
Assert.assertEquals(middleParent.getEntries().get(0).getPermission(),
BasePermission.DELETE);
Assert.assertEquals(middleParent.getEntries().get(0).getSid(), new PrincipalSid(
"ben"));
Assert.assertFalse(((AuditableAccessControlEntry) middleParent.getEntries()
.get(0)).isAuditFailure());
Assert.assertFalse(((AuditableAccessControlEntry) middleParent.getEntries()
.get(0)).isAuditSuccess());
Assert.assertTrue(middleParent.getEntries().get(0).isGranting());
Assert.assertTrue(child.isEntriesInheriting());
Assert.assertEquals(child.getId(), Long.valueOf(3));
Assert.assertEquals(child.getOwner(), new PrincipalSid("ben"));
Assert.assertEquals(child.getEntries().get(0).getId(), Long.valueOf(4));
Assert.assertEquals(child.getEntries().get(0).getPermission(), BasePermission.DELETE);
Assert.assertEquals(child.getEntries().get(0).getSid(), new PrincipalSid("ben"));
Assert.assertFalse(((AuditableAccessControlEntry) child.getEntries().get(0)).isAuditFailure());
Assert.assertFalse(((AuditableAccessControlEntry) child.getEntries().get(0)).isAuditSuccess());
Assert.assertFalse((child.getEntries().get(0)).isGranting());
}
Assert.assertTrue(child.isEntriesInheriting());
Assert.assertEquals(child.getId(), Long.valueOf(3));
Assert.assertEquals(child.getOwner(), new PrincipalSid("ben"));
Assert.assertEquals(child.getEntries().get(0).getId(), Long.valueOf(4));
Assert.assertEquals(child.getEntries().get(0).getPermission(),
BasePermission.DELETE);
Assert.assertEquals(child.getEntries().get(0).getSid(), new PrincipalSid("ben"));
Assert.assertFalse(((AuditableAccessControlEntry) child.getEntries().get(0))
.isAuditFailure());
Assert.assertFalse(((AuditableAccessControlEntry) child.getEntries().get(0))
.isAuditSuccess());
Assert.assertFalse((child.getEntries().get(0)).isGranting());
}
@Test
public void testAllParentsAreRetrievedWhenChildIsLoaded() throws Exception {
String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,103,1,1,1);";
jdbcTemplate.execute(query);
@Test
public void testAllParentsAreRetrievedWhenChildIsLoaded() throws Exception {
String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,103,1,1,1);";
jdbcTemplate.execute(query);
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(101));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(102));
ObjectIdentity middleParent2Oid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(103));
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS,
Long.valueOf(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS,
Long.valueOf(101));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(102));
ObjectIdentity middleParent2Oid = new ObjectIdentityImpl(TARGET_CLASS,
Long.valueOf(103));
// Retrieve the child
Map<ObjectIdentity, Acl> map = this.strategy.readAclsById(Arrays.asList(childOid), null);
// Retrieve the child
Map<ObjectIdentity, Acl> map = this.strategy.readAclsById(
Arrays.asList(childOid), null);
// Check that the child and all its parents were retrieved
Assert.assertNotNull(map.get(childOid));
Assert.assertEquals(childOid, map.get(childOid).getObjectIdentity());
Assert.assertNotNull(map.get(middleParentOid));
Assert.assertEquals(middleParentOid, map.get(middleParentOid).getObjectIdentity());
Assert.assertNotNull(map.get(topParentOid));
Assert.assertEquals(topParentOid, map.get(topParentOid).getObjectIdentity());
// Check that the child and all its parents were retrieved
Assert.assertNotNull(map.get(childOid));
Assert.assertEquals(childOid, map.get(childOid).getObjectIdentity());
Assert.assertNotNull(map.get(middleParentOid));
Assert.assertEquals(middleParentOid, map.get(middleParentOid).getObjectIdentity());
Assert.assertNotNull(map.get(topParentOid));
Assert.assertEquals(topParentOid, map.get(topParentOid).getObjectIdentity());
// The second parent shouldn't have been retrieved
Assert.assertNull(map.get(middleParent2Oid));
}
// The second parent shouldn't have been retrieved
Assert.assertNull(map.get(middleParent2Oid));
}
/**
* Test created from SEC-590.
*/
@Test
public void testReadAllObjectIdentitiesWhenLastElementIsAlreadyCached() throws Exception {
String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,104,null,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (5,2,105,4,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (6,2,106,4,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (7,2,107,5,1,1);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (5,4,0,1,1,1,0,0)";
jdbcTemplate.execute(query);
/**
* Test created from SEC-590.
*/
@Test
public void testReadAllObjectIdentitiesWhenLastElementIsAlreadyCached()
throws Exception {
String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,104,null,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (5,2,105,4,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (6,2,106,4,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (7,2,107,5,1,1);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (5,4,0,1,1,1,0,0)";
jdbcTemplate.execute(query);
ObjectIdentity grandParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(104));
ObjectIdentity parent1Oid = new ObjectIdentityImpl(TARGET_CLASS, new Long(105));
ObjectIdentity parent2Oid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(106));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(107));
ObjectIdentity grandParentOid = new ObjectIdentityImpl(TARGET_CLASS,
new Long(104));
ObjectIdentity parent1Oid = new ObjectIdentityImpl(TARGET_CLASS, new Long(105));
ObjectIdentity parent2Oid = new ObjectIdentityImpl(TARGET_CLASS,
Integer.valueOf(106));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS,
Integer.valueOf(107));
// First lookup only child, thus populating the cache with grandParent, parent1 and child
List<Permission> checkPermission = Arrays.asList(BasePermission.READ);
List<Sid> sids = Arrays.asList(BEN_SID);
List<ObjectIdentity> childOids = Arrays.asList(childOid);
// First lookup only child, thus populating the cache with grandParent, parent1
// and child
List<Permission> checkPermission = Arrays.asList(BasePermission.READ);
List<Sid> sids = Arrays.asList(BEN_SID);
List<ObjectIdentity> childOids = Arrays.asList(childOid);
strategy.setBatchSize(6);
Map<ObjectIdentity, Acl> foundAcls = strategy.readAclsById(childOids, sids);
strategy.setBatchSize(6);
Map<ObjectIdentity, Acl> foundAcls = strategy.readAclsById(childOids, sids);
Acl foundChildAcl = foundAcls.get(childOid);
Assert.assertNotNull(foundChildAcl);
Assert.assertTrue(foundChildAcl.isGranted(checkPermission, sids, false));
Acl foundChildAcl = foundAcls.get(childOid);
Assert.assertNotNull(foundChildAcl);
Assert.assertTrue(foundChildAcl.isGranted(checkPermission, sids, false));
// Search for object identities has to be done in the following order: last element have to be one which
// is already in cache and the element before it must not be stored in cache
List<ObjectIdentity> allOids = Arrays.asList(grandParentOid, parent1Oid, parent2Oid, childOid);
try {
foundAcls = strategy.readAclsById(allOids, sids);
Assert.assertTrue(true);
} catch (NotFoundException notExpected) {
Assert.fail("It shouldn't have thrown NotFoundException");
}
// Search for object identities has to be done in the following order: last
// element have to be one which
// is already in cache and the element before it must not be stored in cache
List<ObjectIdentity> allOids = Arrays.asList(grandParentOid, parent1Oid,
parent2Oid, childOid);
try {
foundAcls = strategy.readAclsById(allOids, sids);
Assert.assertTrue(true);
}
catch (NotFoundException notExpected) {
Assert.fail("It shouldn't have thrown NotFoundException");
}
Acl foundParent2Acl = foundAcls.get(parent2Oid);
Assert.assertNotNull(foundParent2Acl);
Assert.assertTrue(foundParent2Acl.isGranted(checkPermission, sids, false));
}
Acl foundParent2Acl = foundAcls.get(parent2Oid);
Assert.assertNotNull(foundParent2Acl);
Assert.assertTrue(foundParent2Acl.isGranted(checkPermission, sids, false));
}
@Test(expected=IllegalArgumentException.class)
public void nullOwnerIsNotSupported() {
String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,104,null,null,1);";
@Test(expected = IllegalArgumentException.class)
public void nullOwnerIsNotSupported() {
String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,104,null,null,1);";
jdbcTemplate.execute(query);
jdbcTemplate.execute(query);
ObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS, new Long(104));
ObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS, new Long(104));
strategy.readAclsById(Arrays.asList(oid), Arrays.asList(BEN_SID));
}
strategy.readAclsById(Arrays.asList(oid), Arrays.asList(BEN_SID));
}
@Test
public void testCreatePrincipalSid() {
Sid result = strategy.createSid(true, "sid");
@Test
public void testCreatePrincipalSid() {
Sid result = strategy.createSid(true, "sid");
Assert.assertEquals(PrincipalSid.class, result.getClass());
Assert.assertEquals("sid", ((PrincipalSid)result).getPrincipal());
}
Assert.assertEquals(PrincipalSid.class, result.getClass());
Assert.assertEquals("sid", ((PrincipalSid) result).getPrincipal());
}
@Test
public void testCreateGrantedAuthority() {
Sid result = strategy.createSid(false, "sid");
@Test
public void testCreateGrantedAuthority() {
Sid result = strategy.createSid(false, "sid");
Assert.assertEquals(GrantedAuthoritySid.class, result.getClass());
Assert.assertEquals("sid", ((GrantedAuthoritySid)result).getGrantedAuthority());
}
Assert.assertEquals(GrantedAuthoritySid.class, result.getClass());
Assert.assertEquals("sid", ((GrantedAuthoritySid) result).getGrantedAuthority());
}
}

View File

@ -25,21 +25,21 @@ import java.io.IOException;
import javax.sql.DataSource;
/**
* Seeds the database for {@link JdbcMutableAclServiceTests}.
*
* @author Ben Alex
*/
public class DatabaseSeeder {
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
public DatabaseSeeder(DataSource dataSource, Resource resource) throws IOException {
Assert.notNull(dataSource, "dataSource required");
Assert.notNull(resource, "resource required");
public DatabaseSeeder(DataSource dataSource, Resource resource) throws IOException {
Assert.notNull(dataSource, "dataSource required");
Assert.notNull(resource, "resource required");
JdbcTemplate template = new JdbcTemplate(dataSource);
String sql = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
template.execute(sql);
}
JdbcTemplate template = new JdbcTemplate(dataSource);
String sql = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
template.execute(sql);
}
}

View File

@ -48,223 +48,239 @@ import org.springframework.util.ReflectionUtils;
*/
@RunWith(MockitoJUnitRunner.class)
public class EhCacheBasedAclCacheTests {
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
@Mock
private Ehcache cache;
@Captor
private ArgumentCaptor<Element> element;
@Mock
private Ehcache cache;
@Captor
private ArgumentCaptor<Element> element;
private EhCacheBasedAclCache myCache;
private EhCacheBasedAclCache myCache;
private MutableAcl acl;
private MutableAcl acl;
@Before
public void setup() {
myCache = new EhCacheBasedAclCache(cache, new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()), new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_USER")));
@Before
public void setup() {
myCache = new EhCacheBasedAclCache(cache, new DefaultPermissionGrantingStrategy(
new ConsoleAuditLogger()), new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_USER")));
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority(
"ROLE_AUDITING"), new SimpleGrantedAuthority("ROLE_GENERAL"));
acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, new ConsoleAuditLogger());
}
acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy,
new ConsoleAuditLogger());
}
@After
public void cleanup() {
SecurityContextHolder.clearContext();
}
@After
public void cleanup() {
SecurityContextHolder.clearContext();
}
@Test(expected=IllegalArgumentException.class)
public void constructorRejectsNullParameters() throws Exception {
new EhCacheBasedAclCache(null, new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()), new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_USER")));
}
@Test(expected = IllegalArgumentException.class)
public void constructorRejectsNullParameters() throws Exception {
new EhCacheBasedAclCache(null, new DefaultPermissionGrantingStrategy(
new ConsoleAuditLogger()), new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_USER")));
}
@Test
public void methodsRejectNullParameters() throws Exception {
try {
Serializable id = null;
myCache.evictFromCache(id);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
@Test
public void methodsRejectNullParameters() throws Exception {
try {
Serializable id = null;
myCache.evictFromCache(id);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
ObjectIdentity obj = null;
myCache.evictFromCache(obj);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
ObjectIdentity obj = null;
myCache.evictFromCache(obj);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
Serializable id = null;
myCache.getFromCache(id);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
Serializable id = null;
myCache.getFromCache(id);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
ObjectIdentity obj = null;
myCache.getFromCache(obj);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
ObjectIdentity obj = null;
myCache.getFromCache(obj);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
MutableAcl acl = null;
myCache.putInCache(acl);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
try {
MutableAcl acl = null;
myCache.putInCache(acl);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
// SEC-527
@Test
public void testDiskSerializationOfMutableAclObjectInstance() throws Exception {
// Serialization test
File file = File.createTempFile("SEC_TEST", ".object");
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(acl);
oos.close();
// SEC-527
@Test
public void testDiskSerializationOfMutableAclObjectInstance() throws Exception {
// Serialization test
File file = File.createTempFile("SEC_TEST", ".object");
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(acl);
oos.close();
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
MutableAcl retrieved = (MutableAcl) ois.readObject();
ois.close();
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
MutableAcl retrieved = (MutableAcl) ois.readObject();
ois.close();
assertEquals(acl, retrieved);
assertEquals(acl, retrieved);
Object retrieved1 = FieldUtils.getProtectedFieldValue("aclAuthorizationStrategy", retrieved);
assertEquals(null, retrieved1);
Object retrieved1 = FieldUtils.getProtectedFieldValue("aclAuthorizationStrategy",
retrieved);
assertEquals(null, retrieved1);
Object retrieved2 = FieldUtils.getProtectedFieldValue("permissionGrantingStrategy", retrieved);
assertEquals(null, retrieved2);
}
Object retrieved2 = FieldUtils.getProtectedFieldValue(
"permissionGrantingStrategy", retrieved);
assertEquals(null, retrieved2);
}
@Test
public void clearCache() throws Exception {
myCache.clearCache();
@Test
public void clearCache() throws Exception {
myCache.clearCache();
verify(cache).removeAll();
}
verify(cache).removeAll();
}
@Test
public void putInCache() throws Exception {
myCache.putInCache(acl);
@Test
public void putInCache() throws Exception {
myCache.putInCache(acl);
verify(cache, times(2)).put(element.capture());
assertThat(element.getValue().getKey()).isEqualTo(acl.getId());
assertThat(element.getValue().getObjectValue()).isEqualTo(acl);
assertThat(element.getAllValues().get(0).getKey()).isEqualTo(acl.getObjectIdentity());
assertThat(element.getAllValues().get(0).getObjectValue()).isEqualTo(acl);
}
verify(cache, times(2)).put(element.capture());
assertThat(element.getValue().getKey()).isEqualTo(acl.getId());
assertThat(element.getValue().getObjectValue()).isEqualTo(acl);
assertThat(element.getAllValues().get(0).getKey()).isEqualTo(
acl.getObjectIdentity());
assertThat(element.getAllValues().get(0).getObjectValue()).isEqualTo(acl);
}
@Test
public void putInCacheAclWithParent() throws Exception {
Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_GENERAL");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
@Test
public void putInCacheAclWithParent() throws Exception {
Authentication auth = new TestingAuthenticationToken("user", "password",
"ROLE_GENERAL");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identityParent = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(2));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
MutableAcl parentAcl = new AclImpl(identityParent, Long.valueOf(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
acl.setParent(parentAcl);
ObjectIdentity identityParent = new ObjectIdentityImpl(TARGET_CLASS,
Long.valueOf(2));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority(
"ROLE_AUDITING"), new SimpleGrantedAuthority("ROLE_GENERAL"));
MutableAcl parentAcl = new AclImpl(identityParent, Long.valueOf(2),
aclAuthorizationStrategy, new ConsoleAuditLogger());
acl.setParent(parentAcl);
myCache.putInCache(acl);
myCache.putInCache(acl);
verify(cache, times(4)).put(element.capture());
verify(cache, times(4)).put(element.capture());
List<Element> allValues = element.getAllValues();
List<Element> allValues = element.getAllValues();
assertThat(allValues.get(0).getKey()).isEqualTo(parentAcl.getObjectIdentity());
assertThat(allValues.get(0).getObjectValue()).isEqualTo(parentAcl);
assertThat(allValues.get(0).getKey()).isEqualTo(parentAcl.getObjectIdentity());
assertThat(allValues.get(0).getObjectValue()).isEqualTo(parentAcl);
assertThat(allValues.get(1).getKey()).isEqualTo(parentAcl.getId());
assertThat(allValues.get(1).getObjectValue()).isEqualTo(parentAcl);
assertThat(allValues.get(1).getKey()).isEqualTo(parentAcl.getId());
assertThat(allValues.get(1).getObjectValue()).isEqualTo(parentAcl);
assertThat(allValues.get(2).getKey()).isEqualTo(acl.getObjectIdentity());
assertThat(allValues.get(2).getObjectValue()).isEqualTo(acl);
assertThat(allValues.get(2).getKey()).isEqualTo(acl.getObjectIdentity());
assertThat(allValues.get(2).getObjectValue()).isEqualTo(acl);
assertThat(allValues.get(3).getKey()).isEqualTo(acl.getId());
assertThat(allValues.get(3).getObjectValue()).isEqualTo(acl);
}
assertThat(allValues.get(3).getKey()).isEqualTo(acl.getId());
assertThat(allValues.get(3).getObjectValue()).isEqualTo(acl);
}
@Test
public void getFromCacheSerializable() throws Exception {
when(cache.get(acl.getId())).thenReturn(new Element(acl.getId(), acl));
@Test
public void getFromCacheSerializable() throws Exception {
when(cache.get(acl.getId())).thenReturn(new Element(acl.getId(),acl));
assertThat(myCache.getFromCache(acl.getId())).isEqualTo(acl);
}
assertThat(myCache.getFromCache(acl.getId())).isEqualTo(acl);
}
@Test
public void getFromCacheSerializablePopulatesTransient() throws Exception {
when(cache.get(acl.getId())).thenReturn(new Element(acl.getId(), acl));
@Test
public void getFromCacheSerializablePopulatesTransient() throws Exception {
when(cache.get(acl.getId())).thenReturn(new Element(acl.getId(),acl));
myCache.putInCache(acl);
myCache.putInCache(acl);
ReflectionTestUtils.setField(acl, "permissionGrantingStrategy", null);
ReflectionTestUtils.setField(acl, "aclAuthorizationStrategy", null);
ReflectionTestUtils.setField(acl, "permissionGrantingStrategy", null);
ReflectionTestUtils.setField(acl, "aclAuthorizationStrategy", null);
MutableAcl fromCache = myCache.getFromCache(acl.getId());
MutableAcl fromCache = myCache.getFromCache(acl.getId());
assertThat(ReflectionTestUtils.getField(fromCache, "aclAuthorizationStrategy"))
.isNotNull();
assertThat(ReflectionTestUtils.getField(fromCache, "permissionGrantingStrategy"))
.isNotNull();
}
assertThat(ReflectionTestUtils.getField(fromCache, "aclAuthorizationStrategy")).isNotNull();
assertThat(ReflectionTestUtils.getField(fromCache, "permissionGrantingStrategy")).isNotNull();
}
@Test
public void getFromCacheObjectIdentity() throws Exception {
when(cache.get(acl.getId())).thenReturn(new Element(acl.getId(), acl));
@Test
public void getFromCacheObjectIdentity() throws Exception {
when(cache.get(acl.getId())).thenReturn(new Element(acl.getId(),acl));
assertThat(myCache.getFromCache(acl.getId())).isEqualTo(acl);
}
assertThat(myCache.getFromCache(acl.getId())).isEqualTo(acl);
}
@Test
public void getFromCacheObjectIdentityPopulatesTransient() throws Exception {
when(cache.get(acl.getObjectIdentity()))
.thenReturn(new Element(acl.getId(), acl));
@Test
public void getFromCacheObjectIdentityPopulatesTransient() throws Exception {
when(cache.get(acl.getObjectIdentity())).thenReturn(new Element(acl.getId(),acl));
myCache.putInCache(acl);
myCache.putInCache(acl);
ReflectionTestUtils.setField(acl, "permissionGrantingStrategy", null);
ReflectionTestUtils.setField(acl, "aclAuthorizationStrategy", null);
ReflectionTestUtils.setField(acl, "permissionGrantingStrategy", null);
ReflectionTestUtils.setField(acl, "aclAuthorizationStrategy", null);
MutableAcl fromCache = myCache.getFromCache(acl.getObjectIdentity());
MutableAcl fromCache = myCache.getFromCache(acl.getObjectIdentity());
assertThat(ReflectionTestUtils.getField(fromCache, "aclAuthorizationStrategy"))
.isNotNull();
assertThat(ReflectionTestUtils.getField(fromCache, "permissionGrantingStrategy"))
.isNotNull();
}
assertThat(ReflectionTestUtils.getField(fromCache, "aclAuthorizationStrategy")).isNotNull();
assertThat(ReflectionTestUtils.getField(fromCache, "permissionGrantingStrategy")).isNotNull();
}
@Test
public void evictCacheSerializable() throws Exception {
when(cache.get(acl.getObjectIdentity()))
.thenReturn(new Element(acl.getId(), acl));
@Test
public void evictCacheSerializable() throws Exception {
when(cache.get(acl.getObjectIdentity())).thenReturn(new Element(acl.getId(),acl));
myCache.evictFromCache(acl.getObjectIdentity());
myCache.evictFromCache(acl.getObjectIdentity());
verify(cache).remove(acl.getId());
verify(cache).remove(acl.getObjectIdentity());
}
verify(cache).remove(acl.getId());
verify(cache).remove(acl.getObjectIdentity());
}
@Test
public void evictCacheObjectIdentity() throws Exception {
when(cache.get(acl.getId())).thenReturn(new Element(acl.getId(), acl));
@Test
public void evictCacheObjectIdentity() throws Exception {
when(cache.get(acl.getId())).thenReturn(new Element(acl.getId(),acl));
myCache.evictFromCache(acl.getId());
myCache.evictFromCache(acl.getId());
verify(cache).remove(acl.getId());
verify(cache).remove(acl.getObjectIdentity());
}
verify(cache).remove(acl.getId());
verify(cache).remove(acl.getObjectIdentity());
}
}

View File

@ -24,27 +24,29 @@ import org.springframework.security.acls.model.Sid;
@RunWith(MockitoJUnitRunner.class)
public class JdbcAclServiceTests {
@Mock
private DataSource dataSource;
@Mock
private DataSource dataSource;
@Mock
private LookupStrategy lookupStrategy;
@Mock
private LookupStrategy lookupStrategy;
private JdbcAclService aclService;
private JdbcAclService aclService;
@Before
public void setUp() {
aclService = new JdbcAclService(dataSource, lookupStrategy);
}
@Before
public void setUp() {
aclService = new JdbcAclService(dataSource, lookupStrategy);
}
// SEC-1898
@Test(expected = NotFoundException.class)
public void readAclByIdMissingAcl() {
Map<ObjectIdentity, Acl> result = new HashMap<ObjectIdentity, Acl>();
when(lookupStrategy.readAclsById(anyListOf(ObjectIdentity.class), anyListOf(Sid.class))).thenReturn(result);
ObjectIdentity objectIdentity = new ObjectIdentityImpl(Object.class, 1);
List<Sid> sids = Arrays.<Sid> asList(new PrincipalSid("user"));
// SEC-1898
@Test(expected = NotFoundException.class)
public void readAclByIdMissingAcl() {
Map<ObjectIdentity, Acl> result = new HashMap<ObjectIdentity, Acl>();
when(
lookupStrategy.readAclsById(anyListOf(ObjectIdentity.class),
anyListOf(Sid.class))).thenReturn(result);
ObjectIdentity objectIdentity = new ObjectIdentityImpl(Object.class, 1);
List<Sid> sids = Arrays.<Sid> asList(new PrincipalSid("user"));
aclService.readAclById(objectIdentity, sids);
}
aclService.readAclById(objectIdentity, sids);
}
}

View File

@ -60,474 +60,518 @@ import org.springframework.transaction.annotation.Transactional;
* @author Ben Alex
* @author Andrei Stefan
*/
@ContextConfiguration(locations={"/jdbcMutableAclServiceTests-context.xml"})
public class JdbcMutableAclServiceTests extends AbstractTransactionalJUnit4SpringContextTests {
//~ Constant fields ================================================================================================
private static final String TARGET_CLASS = TargetObject.class.getName();
private final Authentication auth = new TestingAuthenticationToken("ben", "ignored","ROLE_ADMINISTRATOR");
public static final String SELECT_ALL_CLASSES = "SELECT * FROM acl_class WHERE class = ?";
//~ Instance fields ================================================================================================
private final ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
private final ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(101));
private final ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(102));
@Autowired
private JdbcMutableAclService jdbcMutableAclService;
@Autowired
private AclCache aclCache;
@Autowired
private LookupStrategy lookupStrategy;
@Autowired
private DataSource dataSource;
@Autowired
private JdbcTemplate jdbcTemplate;
//~ Methods ========================================================================================================
@BeforeTransaction
public void createTables() throws Exception {
try {
new DatabaseSeeder(dataSource, new ClassPathResource("createAclSchema.sql"));
// new DatabaseSeeder(dataSource, new ClassPathResource("createAclSchemaPostgres.sql"));
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@AfterTransaction
public void clearContextAndData() throws Exception {
SecurityContextHolder.clearContext();
jdbcTemplate.execute("drop table acl_entry");
jdbcTemplate.execute("drop table acl_object_identity");
jdbcTemplate.execute("drop table acl_class");
jdbcTemplate.execute("drop table acl_sid");
aclCache.clearCache();
}
@Test
@Transactional
public void testLifecycle() {
SecurityContextHolder.getContext().setAuthentication(auth);
MutableAcl topParent = jdbcMutableAclService.createAcl(topParentOid);
MutableAcl middleParent = jdbcMutableAclService.createAcl(middleParentOid);
MutableAcl child = jdbcMutableAclService.createAcl(childOid);
// Specify the inheritance hierarchy
middleParent.setParent(topParent);
child.setParent(middleParent);
// Now let's add a couple of permissions
topParent.insertAce(0, BasePermission.READ, new PrincipalSid(auth), true);
topParent.insertAce(1, BasePermission.WRITE, new PrincipalSid(auth), false);
middleParent.insertAce(0, BasePermission.DELETE, new PrincipalSid(auth), true);
child.insertAce(0, BasePermission.DELETE, new PrincipalSid(auth), false);
// Explicitly save the changed ACL
jdbcMutableAclService.updateAcl(topParent);
jdbcMutableAclService.updateAcl(middleParent);
jdbcMutableAclService.updateAcl(child);
// Let's check if we can read them back correctly
Map<ObjectIdentity, Acl> map = jdbcMutableAclService.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid));
assertEquals(3, map.size());
// Replace our current objects with their retrieved versions
topParent = (MutableAcl) map.get(topParentOid);
middleParent = (MutableAcl) map.get(middleParentOid);
child = (MutableAcl) map.get(childOid);
// Check the retrieved versions has IDs
assertNotNull(topParent.getId());
assertNotNull(middleParent.getId());
assertNotNull(child.getId());
// Check their parents were correctly persisted
assertNull(topParent.getParentAcl());
assertEquals(topParentOid, middleParent.getParentAcl().getObjectIdentity());
assertEquals(middleParentOid, child.getParentAcl().getObjectIdentity());
// Check their ACEs were correctly persisted
assertEquals(2, topParent.getEntries().size());
assertEquals(1, middleParent.getEntries().size());
assertEquals(1, child.getEntries().size());
// Check the retrieved rights are correct
List<Permission> read = Arrays.asList(BasePermission.READ);
List<Permission> write = Arrays.asList(BasePermission.WRITE);
List<Permission> delete = Arrays.asList(BasePermission.DELETE);
List<Sid> pSid = Arrays.asList((Sid)new PrincipalSid(auth));
assertTrue(topParent.isGranted(read, pSid, false));
assertFalse(topParent.isGranted(write, pSid, false));
assertTrue(middleParent.isGranted(delete, pSid, false));
assertFalse(child.isGranted(delete, pSid, false));
try {
child.isGranted(Arrays.asList(BasePermission.ADMINISTRATION), pSid, false);
fail("Should have thrown NotFoundException");
} catch (NotFoundException expected) {
assertTrue(true);
}
// Now check the inherited rights (when not explicitly overridden) also look OK
assertTrue(child.isGranted(read, pSid, false));
assertFalse(child.isGranted(write, pSid, false));
assertFalse(child.isGranted(delete, pSid, false));
// Next change the child so it doesn't inherit permissions from above
child.setEntriesInheriting(false);
jdbcMutableAclService.updateAcl(child);
child = (MutableAcl) jdbcMutableAclService.readAclById(childOid);
assertFalse(child.isEntriesInheriting());
// Check the child permissions no longer inherit
assertFalse(child.isGranted(delete, pSid, true));
try {
child.isGranted(read, pSid, true);
fail("Should have thrown NotFoundException");
} catch (NotFoundException expected) {
assertTrue(true);
}
try {
child.isGranted(write, pSid, true);
fail("Should have thrown NotFoundException");
} catch (NotFoundException expected) {
assertTrue(true);
}
// Let's add an identical permission to the child, but it'll appear AFTER the current permission, so has no impact
child.insertAce(1, BasePermission.DELETE, new PrincipalSid(auth), true);
// Let's also add another permission to the child
child.insertAce(2, BasePermission.CREATE, new PrincipalSid(auth), true);
// Save the changed child
jdbcMutableAclService.updateAcl(child);
child = (MutableAcl) jdbcMutableAclService.readAclById(childOid);
assertEquals(3, child.getEntries().size());
// Output permissions
for (int i = 0; i < child.getEntries().size(); i++) {
System.out.println(child.getEntries().get(i));
}
// Check the permissions are as they should be
assertFalse(child.isGranted(delete, pSid, true)); // as earlier permission overrode
assertTrue(child.isGranted(Arrays.asList(BasePermission.CREATE), pSid, true));
// Now check the first ACE (index 0) really is DELETE for our Sid and is non-granting
AccessControlEntry entry = child.getEntries().get(0);
assertEquals(BasePermission.DELETE.getMask(), entry.getPermission().getMask());
assertEquals(new PrincipalSid(auth), entry.getSid());
assertFalse(entry.isGranting());
assertNotNull(entry.getId());
// Now delete that first ACE
child.deleteAce(0);
// Save and check it worked
child = jdbcMutableAclService.updateAcl(child);
assertEquals(2, child.getEntries().size());
assertTrue(child.isGranted(delete, pSid, false));
SecurityContextHolder.clearContext();
}
/**
* Test method that demonstrates eviction failure from cache - SEC-676
*/
@Test
@Transactional
public void deleteAclAlsoDeletesChildren() throws Exception {
SecurityContextHolder.getContext().setAuthentication(auth);
jdbcMutableAclService.createAcl(topParentOid);
MutableAcl middleParent = jdbcMutableAclService.createAcl(middleParentOid);
MutableAcl child = jdbcMutableAclService.createAcl(childOid);
child.setParent(middleParent);
jdbcMutableAclService.updateAcl(middleParent);
jdbcMutableAclService.updateAcl(child);
// Check the childOid really is a child of middleParentOid
Acl childAcl = jdbcMutableAclService.readAclById(childOid);
assertEquals(middleParentOid, childAcl.getParentAcl().getObjectIdentity());
// Delete the mid-parent and test if the child was deleted, as well
jdbcMutableAclService.deleteAcl(middleParentOid, true);
try {
jdbcMutableAclService.readAclById(middleParentOid);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
assertTrue(true);
}
try {
jdbcMutableAclService.readAclById(childOid);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
assertTrue(true);
}
Acl acl = jdbcMutableAclService.readAclById(topParentOid);
assertNotNull(acl);
assertEquals(((MutableAcl) acl).getObjectIdentity(), topParentOid);
}
@Test
public void constructorRejectsNullParameters() throws Exception {
try {
new JdbcMutableAclService(null, lookupStrategy, aclCache);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
new JdbcMutableAclService(dataSource, null, aclCache);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
new JdbcMutableAclService(dataSource, lookupStrategy, null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
@Test
public void createAclRejectsNullParameter() throws Exception {
try {
jdbcMutableAclService.createAcl(null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
@Test
@Transactional
public void createAclForADuplicateDomainObject() throws Exception {
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity duplicateOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
jdbcMutableAclService.createAcl(duplicateOid);
// Try to add the same object second time
try {
jdbcMutableAclService.createAcl(duplicateOid);
fail("It should have thrown AlreadyExistsException");
}
catch (AlreadyExistsException expected) {
}
}
@Test
@Transactional
public void deleteAclRejectsNullParameters() throws Exception {
try {
jdbcMutableAclService.deleteAcl(null, true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
@Test
@Transactional
public void deleteAclWithChildrenThrowsException() throws Exception {
SecurityContextHolder.getContext().setAuthentication(auth);
MutableAcl parent = jdbcMutableAclService.createAcl(topParentOid);
MutableAcl child = jdbcMutableAclService.createAcl(middleParentOid);
// Specify the inheritance hierarchy
child.setParent(parent);
jdbcMutableAclService.updateAcl(child);
try {
jdbcMutableAclService.setForeignKeysInDatabase(false); // switch on FK checking in the class, not database
jdbcMutableAclService.deleteAcl(topParentOid, false);
fail("It should have thrown ChildrenExistException");
}
catch (ChildrenExistException expected) {
} finally {
jdbcMutableAclService.setForeignKeysInDatabase(true); // restore to the default
}
}
@Test
@Transactional
public void deleteAclRemovesRowsFromDatabase() throws Exception {
SecurityContextHolder.getContext().setAuthentication(auth);
MutableAcl child = jdbcMutableAclService.createAcl(childOid);
child.insertAce(0, BasePermission.DELETE, new PrincipalSid(auth), false);
jdbcMutableAclService.updateAcl(child);
// Remove the child and check all related database rows were removed accordingly
jdbcMutableAclService.deleteAcl(childOid, false);
assertEquals(1, jdbcTemplate.queryForList(SELECT_ALL_CLASSES, new Object[] {TARGET_CLASS} ).size());
assertEquals(0, jdbcTemplate.queryForList("select * from acl_object_identity").size());
assertEquals(0, jdbcTemplate.queryForList("select * from acl_entry").size());
// Check the cache
assertNull(aclCache.getFromCache(childOid));
assertNull(aclCache.getFromCache(Long.valueOf(102)));
}
/** SEC-1107 */
@Test
@Transactional
public void identityWithIntegerIdIsSupportedByCreateAcl() throws Exception {
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(101));
jdbcMutableAclService.createAcl(oid);
assertNotNull(jdbcMutableAclService.readAclById(new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(101))));
}
/**
* SEC-655
*/
@Test
@Transactional
public void childrenAreClearedFromCacheWhenParentIsUpdated() throws Exception {
Authentication auth = new TestingAuthenticationToken("ben", "ignored","ROLE_ADMINISTRATOR");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity parentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(104));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(105));
MutableAcl parent = jdbcMutableAclService.createAcl(parentOid);
MutableAcl child = jdbcMutableAclService.createAcl(childOid);
child.setParent(parent);
jdbcMutableAclService.updateAcl(child);
parent = (AclImpl) jdbcMutableAclService.readAclById(parentOid);
parent.insertAce(0, BasePermission.READ, new PrincipalSid("ben"), true);
jdbcMutableAclService.updateAcl(parent);
parent = (AclImpl) jdbcMutableAclService.readAclById(parentOid);
parent.insertAce(1, BasePermission.READ, new PrincipalSid("scott"), true);
jdbcMutableAclService.updateAcl(parent);
child = (MutableAcl) jdbcMutableAclService.readAclById(childOid);
parent = (MutableAcl) child.getParentAcl();
assertEquals("Fails because child has a stale reference to its parent", 2, parent.getEntries().size());
assertEquals(1, parent.getEntries().get(0).getPermission().getMask());
assertEquals(new PrincipalSid("ben"), parent.getEntries().get(0).getSid());
assertEquals(1, parent.getEntries().get(1).getPermission().getMask());
assertEquals(new PrincipalSid("scott"), parent.getEntries().get(1).getSid());
}
/**
* SEC-655
*/
@Test
@Transactional
public void childrenAreClearedFromCacheWhenParentisUpdated2() throws Exception {
Authentication auth = new TestingAuthenticationToken("system", "secret","ROLE_IGNORED");
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentityImpl rootObject = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(1));
MutableAcl parent = jdbcMutableAclService.createAcl(rootObject);
MutableAcl child = jdbcMutableAclService.createAcl(new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(2)));
child.setParent(parent);
jdbcMutableAclService.updateAcl(child);
parent.insertAce(0, BasePermission.ADMINISTRATION, new GrantedAuthoritySid("ROLE_ADMINISTRATOR"), true);
jdbcMutableAclService.updateAcl(parent);
parent.insertAce(1, BasePermission.DELETE, new PrincipalSid("terry"), true);
jdbcMutableAclService.updateAcl(parent);
child = (MutableAcl) jdbcMutableAclService.readAclById(new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(2)));
parent = (MutableAcl) child.getParentAcl();
assertEquals(2, parent.getEntries().size());
assertEquals(16, parent.getEntries().get(0).getPermission().getMask());
assertEquals(new GrantedAuthoritySid("ROLE_ADMINISTRATOR"), parent.getEntries().get(0).getSid());
assertEquals(8, parent.getEntries().get(1).getPermission().getMask());
assertEquals(new PrincipalSid("terry"), parent.getEntries().get(1).getSid());
}
@Test
@Transactional
public void cumulativePermissions() {
Authentication auth = new TestingAuthenticationToken("ben", "ignored", "ROLE_ADMINISTRATOR");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(110));
MutableAcl topParent = jdbcMutableAclService.createAcl(topParentOid);
// Add an ACE permission entry
Permission cm = new CumulativePermission().set(BasePermission.READ).set(BasePermission.ADMINISTRATION);
assertEquals(17, cm.getMask());
Sid benSid = new PrincipalSid(auth);
topParent.insertAce(0, cm, benSid, true);
assertEquals(1, topParent.getEntries().size());
// Explicitly save the changed ACL
topParent = jdbcMutableAclService.updateAcl(topParent);
// Check the mask was retrieved correctly
assertEquals(17, topParent.getEntries().get(0).getPermission().getMask());
assertTrue(topParent.isGranted(Arrays.asList(cm), Arrays.asList(benSid), true));
SecurityContextHolder.clearContext();
}
@Test
public void testProcessingCustomSid() {
CustomJdbcMutableAclService customJdbcMutableAclService = spy(new CustomJdbcMutableAclService(dataSource,
lookupStrategy, aclCache));
CustomSid customSid = new CustomSid("Custom sid");
when(customJdbcMutableAclService.createOrRetrieveSidPrimaryKey("Custom sid", false, false)).thenReturn(1L);
Long result = customJdbcMutableAclService.createOrRetrieveSidPrimaryKey(customSid, false);
assertEquals(result, new Long(1L));
}
/**
* This class needed to show how to extend {@link JdbcMutableAclService} for processing
* custom {@link Sid} implementations
*/
private class CustomJdbcMutableAclService extends JdbcMutableAclService {
private CustomJdbcMutableAclService(DataSource dataSource, LookupStrategy lookupStrategy, AclCache aclCache) {
super(dataSource, lookupStrategy, aclCache);
}
@Override
protected Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) {
String sidName;
boolean isPrincipal = false;
if (sid instanceof CustomSid) {
sidName = ((CustomSid)sid).getSid();
} else if (sid instanceof GrantedAuthoritySid) {
sidName = ((GrantedAuthoritySid)sid).getGrantedAuthority();
} else {
sidName = ((PrincipalSid)sid).getPrincipal();
isPrincipal = true;
}
return createOrRetrieveSidPrimaryKey(sidName, isPrincipal, allowCreate);
}
}
@ContextConfiguration(locations = { "/jdbcMutableAclServiceTests-context.xml" })
public class JdbcMutableAclServiceTests extends
AbstractTransactionalJUnit4SpringContextTests {
// ~ Constant fields
// ================================================================================================
private static final String TARGET_CLASS = TargetObject.class.getName();
private final Authentication auth = new TestingAuthenticationToken("ben", "ignored",
"ROLE_ADMINISTRATOR");
public static final String SELECT_ALL_CLASSES = "SELECT * FROM acl_class WHERE class = ?";
// ~ Instance fields
// ================================================================================================
private final ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS,
Long.valueOf(100));
private final ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS,
Long.valueOf(101));
private final ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS,
Long.valueOf(102));
@Autowired
private JdbcMutableAclService jdbcMutableAclService;
@Autowired
private AclCache aclCache;
@Autowired
private LookupStrategy lookupStrategy;
@Autowired
private DataSource dataSource;
@Autowired
private JdbcTemplate jdbcTemplate;
// ~ Methods
// ========================================================================================================
@BeforeTransaction
public void createTables() throws Exception {
try {
new DatabaseSeeder(dataSource, new ClassPathResource("createAclSchema.sql"));
// new DatabaseSeeder(dataSource, new
// ClassPathResource("createAclSchemaPostgres.sql"));
}
catch (Exception e) {
e.printStackTrace();
throw e;
}
}
@AfterTransaction
public void clearContextAndData() throws Exception {
SecurityContextHolder.clearContext();
jdbcTemplate.execute("drop table acl_entry");
jdbcTemplate.execute("drop table acl_object_identity");
jdbcTemplate.execute("drop table acl_class");
jdbcTemplate.execute("drop table acl_sid");
aclCache.clearCache();
}
@Test
@Transactional
public void testLifecycle() {
SecurityContextHolder.getContext().setAuthentication(auth);
MutableAcl topParent = jdbcMutableAclService.createAcl(topParentOid);
MutableAcl middleParent = jdbcMutableAclService.createAcl(middleParentOid);
MutableAcl child = jdbcMutableAclService.createAcl(childOid);
// Specify the inheritance hierarchy
middleParent.setParent(topParent);
child.setParent(middleParent);
// Now let's add a couple of permissions
topParent.insertAce(0, BasePermission.READ, new PrincipalSid(auth), true);
topParent.insertAce(1, BasePermission.WRITE, new PrincipalSid(auth), false);
middleParent.insertAce(0, BasePermission.DELETE, new PrincipalSid(auth), true);
child.insertAce(0, BasePermission.DELETE, new PrincipalSid(auth), false);
// Explicitly save the changed ACL
jdbcMutableAclService.updateAcl(topParent);
jdbcMutableAclService.updateAcl(middleParent);
jdbcMutableAclService.updateAcl(child);
// Let's check if we can read them back correctly
Map<ObjectIdentity, Acl> map = jdbcMutableAclService.readAclsById(Arrays.asList(
topParentOid, middleParentOid, childOid));
assertEquals(3, map.size());
// Replace our current objects with their retrieved versions
topParent = (MutableAcl) map.get(topParentOid);
middleParent = (MutableAcl) map.get(middleParentOid);
child = (MutableAcl) map.get(childOid);
// Check the retrieved versions has IDs
assertNotNull(topParent.getId());
assertNotNull(middleParent.getId());
assertNotNull(child.getId());
// Check their parents were correctly persisted
assertNull(topParent.getParentAcl());
assertEquals(topParentOid, middleParent.getParentAcl().getObjectIdentity());
assertEquals(middleParentOid, child.getParentAcl().getObjectIdentity());
// Check their ACEs were correctly persisted
assertEquals(2, topParent.getEntries().size());
assertEquals(1, middleParent.getEntries().size());
assertEquals(1, child.getEntries().size());
// Check the retrieved rights are correct
List<Permission> read = Arrays.asList(BasePermission.READ);
List<Permission> write = Arrays.asList(BasePermission.WRITE);
List<Permission> delete = Arrays.asList(BasePermission.DELETE);
List<Sid> pSid = Arrays.asList((Sid) new PrincipalSid(auth));
assertTrue(topParent.isGranted(read, pSid, false));
assertFalse(topParent.isGranted(write, pSid, false));
assertTrue(middleParent.isGranted(delete, pSid, false));
assertFalse(child.isGranted(delete, pSid, false));
try {
child.isGranted(Arrays.asList(BasePermission.ADMINISTRATION), pSid, false);
fail("Should have thrown NotFoundException");
}
catch (NotFoundException expected) {
assertTrue(true);
}
// Now check the inherited rights (when not explicitly overridden) also look OK
assertTrue(child.isGranted(read, pSid, false));
assertFalse(child.isGranted(write, pSid, false));
assertFalse(child.isGranted(delete, pSid, false));
// Next change the child so it doesn't inherit permissions from above
child.setEntriesInheriting(false);
jdbcMutableAclService.updateAcl(child);
child = (MutableAcl) jdbcMutableAclService.readAclById(childOid);
assertFalse(child.isEntriesInheriting());
// Check the child permissions no longer inherit
assertFalse(child.isGranted(delete, pSid, true));
try {
child.isGranted(read, pSid, true);
fail("Should have thrown NotFoundException");
}
catch (NotFoundException expected) {
assertTrue(true);
}
try {
child.isGranted(write, pSid, true);
fail("Should have thrown NotFoundException");
}
catch (NotFoundException expected) {
assertTrue(true);
}
// Let's add an identical permission to the child, but it'll appear AFTER the
// current permission, so has no impact
child.insertAce(1, BasePermission.DELETE, new PrincipalSid(auth), true);
// Let's also add another permission to the child
child.insertAce(2, BasePermission.CREATE, new PrincipalSid(auth), true);
// Save the changed child
jdbcMutableAclService.updateAcl(child);
child = (MutableAcl) jdbcMutableAclService.readAclById(childOid);
assertEquals(3, child.getEntries().size());
// Output permissions
for (int i = 0; i < child.getEntries().size(); i++) {
System.out.println(child.getEntries().get(i));
}
// Check the permissions are as they should be
assertFalse(child.isGranted(delete, pSid, true)); // as earlier permission
// overrode
assertTrue(child.isGranted(Arrays.asList(BasePermission.CREATE), pSid, true));
// Now check the first ACE (index 0) really is DELETE for our Sid and is
// non-granting
AccessControlEntry entry = child.getEntries().get(0);
assertEquals(BasePermission.DELETE.getMask(), entry.getPermission().getMask());
assertEquals(new PrincipalSid(auth), entry.getSid());
assertFalse(entry.isGranting());
assertNotNull(entry.getId());
// Now delete that first ACE
child.deleteAce(0);
// Save and check it worked
child = jdbcMutableAclService.updateAcl(child);
assertEquals(2, child.getEntries().size());
assertTrue(child.isGranted(delete, pSid, false));
SecurityContextHolder.clearContext();
}
/**
* Test method that demonstrates eviction failure from cache - SEC-676
*/
@Test
@Transactional
public void deleteAclAlsoDeletesChildren() throws Exception {
SecurityContextHolder.getContext().setAuthentication(auth);
jdbcMutableAclService.createAcl(topParentOid);
MutableAcl middleParent = jdbcMutableAclService.createAcl(middleParentOid);
MutableAcl child = jdbcMutableAclService.createAcl(childOid);
child.setParent(middleParent);
jdbcMutableAclService.updateAcl(middleParent);
jdbcMutableAclService.updateAcl(child);
// Check the childOid really is a child of middleParentOid
Acl childAcl = jdbcMutableAclService.readAclById(childOid);
assertEquals(middleParentOid, childAcl.getParentAcl().getObjectIdentity());
// Delete the mid-parent and test if the child was deleted, as well
jdbcMutableAclService.deleteAcl(middleParentOid, true);
try {
jdbcMutableAclService.readAclById(middleParentOid);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
assertTrue(true);
}
try {
jdbcMutableAclService.readAclById(childOid);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
assertTrue(true);
}
Acl acl = jdbcMutableAclService.readAclById(topParentOid);
assertNotNull(acl);
assertEquals(((MutableAcl) acl).getObjectIdentity(), topParentOid);
}
@Test
public void constructorRejectsNullParameters() throws Exception {
try {
new JdbcMutableAclService(null, lookupStrategy, aclCache);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
new JdbcMutableAclService(dataSource, null, aclCache);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
new JdbcMutableAclService(dataSource, lookupStrategy, null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
@Test
public void createAclRejectsNullParameter() throws Exception {
try {
jdbcMutableAclService.createAcl(null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
@Test
@Transactional
public void createAclForADuplicateDomainObject() throws Exception {
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity duplicateOid = new ObjectIdentityImpl(TARGET_CLASS,
Long.valueOf(100));
jdbcMutableAclService.createAcl(duplicateOid);
// Try to add the same object second time
try {
jdbcMutableAclService.createAcl(duplicateOid);
fail("It should have thrown AlreadyExistsException");
}
catch (AlreadyExistsException expected) {
}
}
@Test
@Transactional
public void deleteAclRejectsNullParameters() throws Exception {
try {
jdbcMutableAclService.deleteAcl(null, true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
@Test
@Transactional
public void deleteAclWithChildrenThrowsException() throws Exception {
SecurityContextHolder.getContext().setAuthentication(auth);
MutableAcl parent = jdbcMutableAclService.createAcl(topParentOid);
MutableAcl child = jdbcMutableAclService.createAcl(middleParentOid);
// Specify the inheritance hierarchy
child.setParent(parent);
jdbcMutableAclService.updateAcl(child);
try {
jdbcMutableAclService.setForeignKeysInDatabase(false); // switch on FK
// checking in the
// class, not database
jdbcMutableAclService.deleteAcl(topParentOid, false);
fail("It should have thrown ChildrenExistException");
}
catch (ChildrenExistException expected) {
}
finally {
jdbcMutableAclService.setForeignKeysInDatabase(true); // restore to the
// default
}
}
@Test
@Transactional
public void deleteAclRemovesRowsFromDatabase() throws Exception {
SecurityContextHolder.getContext().setAuthentication(auth);
MutableAcl child = jdbcMutableAclService.createAcl(childOid);
child.insertAce(0, BasePermission.DELETE, new PrincipalSid(auth), false);
jdbcMutableAclService.updateAcl(child);
// Remove the child and check all related database rows were removed accordingly
jdbcMutableAclService.deleteAcl(childOid, false);
assertEquals(
1,
jdbcTemplate.queryForList(SELECT_ALL_CLASSES,
new Object[] { TARGET_CLASS }).size());
assertEquals(0, jdbcTemplate.queryForList("select * from acl_object_identity")
.size());
assertEquals(0, jdbcTemplate.queryForList("select * from acl_entry").size());
// Check the cache
assertNull(aclCache.getFromCache(childOid));
assertNull(aclCache.getFromCache(Long.valueOf(102)));
}
/** SEC-1107 */
@Test
@Transactional
public void identityWithIntegerIdIsSupportedByCreateAcl() throws Exception {
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(101));
jdbcMutableAclService.createAcl(oid);
assertNotNull(jdbcMutableAclService.readAclById(new ObjectIdentityImpl(
TARGET_CLASS, Long.valueOf(101))));
}
/**
* SEC-655
*/
@Test
@Transactional
public void childrenAreClearedFromCacheWhenParentIsUpdated() throws Exception {
Authentication auth = new TestingAuthenticationToken("ben", "ignored",
"ROLE_ADMINISTRATOR");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity parentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(104));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(105));
MutableAcl parent = jdbcMutableAclService.createAcl(parentOid);
MutableAcl child = jdbcMutableAclService.createAcl(childOid);
child.setParent(parent);
jdbcMutableAclService.updateAcl(child);
parent = (AclImpl) jdbcMutableAclService.readAclById(parentOid);
parent.insertAce(0, BasePermission.READ, new PrincipalSid("ben"), true);
jdbcMutableAclService.updateAcl(parent);
parent = (AclImpl) jdbcMutableAclService.readAclById(parentOid);
parent.insertAce(1, BasePermission.READ, new PrincipalSid("scott"), true);
jdbcMutableAclService.updateAcl(parent);
child = (MutableAcl) jdbcMutableAclService.readAclById(childOid);
parent = (MutableAcl) child.getParentAcl();
assertEquals("Fails because child has a stale reference to its parent", 2, parent
.getEntries().size());
assertEquals(1, parent.getEntries().get(0).getPermission().getMask());
assertEquals(new PrincipalSid("ben"), parent.getEntries().get(0).getSid());
assertEquals(1, parent.getEntries().get(1).getPermission().getMask());
assertEquals(new PrincipalSid("scott"), parent.getEntries().get(1).getSid());
}
/**
* SEC-655
*/
@Test
@Transactional
public void childrenAreClearedFromCacheWhenParentisUpdated2() throws Exception {
Authentication auth = new TestingAuthenticationToken("system", "secret",
"ROLE_IGNORED");
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentityImpl rootObject = new ObjectIdentityImpl(TARGET_CLASS,
Long.valueOf(1));
MutableAcl parent = jdbcMutableAclService.createAcl(rootObject);
MutableAcl child = jdbcMutableAclService.createAcl(new ObjectIdentityImpl(
TARGET_CLASS, Long.valueOf(2)));
child.setParent(parent);
jdbcMutableAclService.updateAcl(child);
parent.insertAce(0, BasePermission.ADMINISTRATION, new GrantedAuthoritySid(
"ROLE_ADMINISTRATOR"), true);
jdbcMutableAclService.updateAcl(parent);
parent.insertAce(1, BasePermission.DELETE, new PrincipalSid("terry"), true);
jdbcMutableAclService.updateAcl(parent);
child = (MutableAcl) jdbcMutableAclService.readAclById(new ObjectIdentityImpl(
TARGET_CLASS, Long.valueOf(2)));
parent = (MutableAcl) child.getParentAcl();
assertEquals(2, parent.getEntries().size());
assertEquals(16, parent.getEntries().get(0).getPermission().getMask());
assertEquals(new GrantedAuthoritySid("ROLE_ADMINISTRATOR"), parent.getEntries()
.get(0).getSid());
assertEquals(8, parent.getEntries().get(1).getPermission().getMask());
assertEquals(new PrincipalSid("terry"), parent.getEntries().get(1).getSid());
}
@Test
@Transactional
public void cumulativePermissions() {
Authentication auth = new TestingAuthenticationToken("ben", "ignored",
"ROLE_ADMINISTRATOR");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS,
Long.valueOf(110));
MutableAcl topParent = jdbcMutableAclService.createAcl(topParentOid);
// Add an ACE permission entry
Permission cm = new CumulativePermission().set(BasePermission.READ).set(
BasePermission.ADMINISTRATION);
assertEquals(17, cm.getMask());
Sid benSid = new PrincipalSid(auth);
topParent.insertAce(0, cm, benSid, true);
assertEquals(1, topParent.getEntries().size());
// Explicitly save the changed ACL
topParent = jdbcMutableAclService.updateAcl(topParent);
// Check the mask was retrieved correctly
assertEquals(17, topParent.getEntries().get(0).getPermission().getMask());
assertTrue(topParent.isGranted(Arrays.asList(cm), Arrays.asList(benSid), true));
SecurityContextHolder.clearContext();
}
@Test
public void testProcessingCustomSid() {
CustomJdbcMutableAclService customJdbcMutableAclService = spy(new CustomJdbcMutableAclService(
dataSource, lookupStrategy, aclCache));
CustomSid customSid = new CustomSid("Custom sid");
when(
customJdbcMutableAclService.createOrRetrieveSidPrimaryKey("Custom sid",
false, false)).thenReturn(1L);
Long result = customJdbcMutableAclService.createOrRetrieveSidPrimaryKey(
customSid, false);
assertEquals(result, new Long(1L));
}
/**
* This class needed to show how to extend {@link JdbcMutableAclService} for
* processing custom {@link Sid} implementations
*/
private class CustomJdbcMutableAclService extends JdbcMutableAclService {
private CustomJdbcMutableAclService(DataSource dataSource,
LookupStrategy lookupStrategy, AclCache aclCache) {
super(dataSource, lookupStrategy, aclCache);
}
@Override
protected Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) {
String sidName;
boolean isPrincipal = false;
if (sid instanceof CustomSid) {
sidName = ((CustomSid) sid).getSid();
}
else if (sid instanceof GrantedAuthoritySid) {
sidName = ((GrantedAuthoritySid) sid).getGrantedAuthority();
}
else {
sidName = ((PrincipalSid) sid).getPrincipal();
isPrincipal = true;
}
return createOrRetrieveSidPrimaryKey(sidName, isPrincipal, allowCreate);
}
}
}

View File

@ -26,117 +26,131 @@ import static org.junit.Assert.*;
* @author Marten Deinum
*/
public class SpringCacheBasedAclCacheTests {
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
private static CacheManager cacheManager;
private static CacheManager cacheManager;
@BeforeClass
public static void initCacheManaer() {
cacheManager = new ConcurrentMapCacheManager();
// Use disk caching immediately (to test for serialization issue reported in SEC-527)
cacheManager.getCache("springcasebasedacltests");
}
@BeforeClass
public static void initCacheManaer() {
cacheManager = new ConcurrentMapCacheManager();
// Use disk caching immediately (to test for serialization issue reported in
// SEC-527)
cacheManager.getCache("springcasebasedacltests");
}
@After
public void clearContext() {
SecurityContextHolder.clearContext();
}
@After
public void clearContext() {
SecurityContextHolder.clearContext();
}
private Cache getCache() {
Cache cache = cacheManager.getCache("springcasebasedacltests");
cache.clear();
return cache;
}
private Cache getCache() {
Cache cache = cacheManager.getCache("springcasebasedacltests");
cache.clear();
return cache;
}
@Test(expected=IllegalArgumentException.class)
public void constructorRejectsNullParameters() throws Exception {
new SpringCacheBasedAclCache(null, null, null);
}
@Test(expected = IllegalArgumentException.class)
public void constructorRejectsNullParameters() throws Exception {
new SpringCacheBasedAclCache(null, null, null);
}
@SuppressWarnings("rawtypes")
@Test
public void cacheOperationsAclWithoutParent() throws Exception {
Cache cache = getCache();
Map realCache = (Map) cache.getNativeCache();
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
AuditLogger auditLogger = new ConsoleAuditLogger();
@SuppressWarnings("rawtypes")
@Test
public void cacheOperationsAclWithoutParent() throws Exception {
Cache cache = getCache();
Map realCache = (Map) cache.getNativeCache();
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority(
"ROLE_AUDITING"), new SimpleGrantedAuthority("ROLE_GENERAL"));
AuditLogger auditLogger = new ConsoleAuditLogger();
PermissionGrantingStrategy permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(auditLogger);
SpringCacheBasedAclCache myCache = new SpringCacheBasedAclCache(cache, permissionGrantingStrategy, aclAuthorizationStrategy);
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, auditLogger);
PermissionGrantingStrategy permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(
auditLogger);
SpringCacheBasedAclCache myCache = new SpringCacheBasedAclCache(cache,
permissionGrantingStrategy, aclAuthorizationStrategy);
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy,
auditLogger);
assertEquals(0, realCache.size());
myCache.putInCache(acl);
assertEquals(0, realCache.size());
myCache.putInCache(acl);
// Check we can get from cache the same objects we put in
assertEquals(myCache.getFromCache(Long.valueOf(1)), acl);
assertEquals(myCache.getFromCache(identity), acl);
// Check we can get from cache the same objects we put in
assertEquals(myCache.getFromCache(Long.valueOf(1)), acl);
assertEquals(myCache.getFromCache(identity), acl);
// Put another object in cache
ObjectIdentity identity2 = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(101));
MutableAcl acl2 = new AclImpl(identity2, Long.valueOf(2), aclAuthorizationStrategy, new ConsoleAuditLogger());
// Put another object in cache
ObjectIdentity identity2 = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(101));
MutableAcl acl2 = new AclImpl(identity2, Long.valueOf(2),
aclAuthorizationStrategy, new ConsoleAuditLogger());
myCache.putInCache(acl2);
myCache.putInCache(acl2);
// Try to evict an entry that doesn't exist
myCache.evictFromCache(Long.valueOf(3));
myCache.evictFromCache(new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(102)));
assertEquals(realCache.size(), 4);
// Try to evict an entry that doesn't exist
myCache.evictFromCache(Long.valueOf(3));
myCache.evictFromCache(new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(102)));
assertEquals(realCache.size(), 4);
myCache.evictFromCache(Long.valueOf(1));
assertEquals(realCache.size(), 2);
myCache.evictFromCache(Long.valueOf(1));
assertEquals(realCache.size(), 2);
// Check the second object inserted
assertEquals(myCache.getFromCache(Long.valueOf(2)), acl2);
assertEquals(myCache.getFromCache(identity2), acl2);
// Check the second object inserted
assertEquals(myCache.getFromCache(Long.valueOf(2)), acl2);
assertEquals(myCache.getFromCache(identity2), acl2);
myCache.evictFromCache(identity2);
assertEquals(realCache.size(), 0);
}
myCache.evictFromCache(identity2);
assertEquals(realCache.size(), 0);
}
@SuppressWarnings("rawtypes")
@Test
public void cacheOperationsAclWithParent() throws Exception {
Cache cache = getCache();
Map realCache = (Map) cache.getNativeCache();
@SuppressWarnings("rawtypes")
@Test
public void cacheOperationsAclWithParent() throws Exception {
Cache cache = getCache();
Map realCache = (Map) cache.getNativeCache();
Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_GENERAL");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
Authentication auth = new TestingAuthenticationToken("user", "password",
"ROLE_GENERAL");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(1));
ObjectIdentity identityParent = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(2));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
new SimpleGrantedAuthority("ROLE_GENERAL"));
AuditLogger auditLogger = new ConsoleAuditLogger();
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(1));
ObjectIdentity identityParent = new ObjectIdentityImpl(TARGET_CLASS,
Long.valueOf(2));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority(
"ROLE_AUDITING"), new SimpleGrantedAuthority("ROLE_GENERAL"));
AuditLogger auditLogger = new ConsoleAuditLogger();
PermissionGrantingStrategy permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(auditLogger);
SpringCacheBasedAclCache myCache = new SpringCacheBasedAclCache(cache, permissionGrantingStrategy, aclAuthorizationStrategy);
PermissionGrantingStrategy permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(
auditLogger);
SpringCacheBasedAclCache myCache = new SpringCacheBasedAclCache(cache,
permissionGrantingStrategy, aclAuthorizationStrategy);
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy, auditLogger);
MutableAcl parentAcl = new AclImpl(identityParent, Long.valueOf(2), aclAuthorizationStrategy, auditLogger);
MutableAcl acl = new AclImpl(identity, Long.valueOf(1), aclAuthorizationStrategy,
auditLogger);
MutableAcl parentAcl = new AclImpl(identityParent, Long.valueOf(2),
aclAuthorizationStrategy, auditLogger);
acl.setParent(parentAcl);
acl.setParent(parentAcl);
assertEquals(0, realCache.size());
myCache.putInCache(acl);
assertEquals(realCache.size(), 4);
assertEquals(0, realCache.size());
myCache.putInCache(acl);
assertEquals(realCache.size(), 4);
// Check we can get from cache the same objects we put in
AclImpl aclFromCache = (AclImpl) myCache.getFromCache(Long.valueOf(1));
assertEquals(acl, aclFromCache);
// SEC-951 check transient fields are set on parent
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "aclAuthorizationStrategy"));
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(), "permissionGrantingStrategy"));
assertEquals(acl, myCache.getFromCache(identity));
assertNotNull(FieldUtils.getFieldValue(aclFromCache, "aclAuthorizationStrategy"));
AclImpl parentAclFromCache = (AclImpl) myCache.getFromCache(Long.valueOf(2));
assertEquals(parentAcl, parentAclFromCache);
assertNotNull(FieldUtils.getFieldValue(parentAclFromCache, "aclAuthorizationStrategy"));
assertEquals(parentAcl, myCache.getFromCache(identityParent));
}
// Check we can get from cache the same objects we put in
AclImpl aclFromCache = (AclImpl) myCache.getFromCache(Long.valueOf(1));
assertEquals(acl, aclFromCache);
// SEC-951 check transient fields are set on parent
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(),
"aclAuthorizationStrategy"));
assertNotNull(FieldUtils.getFieldValue(aclFromCache.getParentAcl(),
"permissionGrantingStrategy"));
assertEquals(acl, myCache.getFromCache(identity));
assertNotNull(FieldUtils.getFieldValue(aclFromCache, "aclAuthorizationStrategy"));
AclImpl parentAclFromCache = (AclImpl) myCache.getFromCache(Long.valueOf(2));
assertEquals(parentAcl, parentAclFromCache);
assertNotNull(FieldUtils.getFieldValue(parentAclFromCache,
"aclAuthorizationStrategy"));
assertEquals(parentAcl, myCache.getFromCache(identityParent));
}
}

View File

@ -8,17 +8,17 @@ import org.springframework.security.acls.model.Sid;
*/
public class CustomSid implements Sid {
private String sid;
private String sid;
public CustomSid(String sid) {
this.sid = sid;
}
public CustomSid(String sid) {
this.sid = sid;
}
public String getSid() {
return sid;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public void setSid(String sid) {
this.sid = sid;
}
}

View File

@ -27,41 +27,44 @@ import org.springframework.security.core.authority.AuthorityUtils;
*/
@SuppressWarnings("unchecked")
public class SidRetrievalStrategyTests {
Authentication authentication = new TestingAuthenticationToken("scott", "password", "A", "B", "C");
Authentication authentication = new TestingAuthenticationToken("scott", "password",
"A", "B", "C");
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
@Test
public void correctSidsAreRetrieved() throws Exception {
SidRetrievalStrategy retrStrategy = new SidRetrievalStrategyImpl();
List<Sid> sids = retrStrategy.getSids(authentication);
@Test
public void correctSidsAreRetrieved() throws Exception {
SidRetrievalStrategy retrStrategy = new SidRetrievalStrategyImpl();
List<Sid> sids = retrStrategy.getSids(authentication);
assertNotNull(sids);
assertEquals(4, sids.size());
assertNotNull(sids.get(0));
assertTrue(sids.get(0) instanceof PrincipalSid);
assertNotNull(sids);
assertEquals(4, sids.size());
assertNotNull(sids.get(0));
assertTrue(sids.get(0) instanceof PrincipalSid);
for (int i = 1; i < sids.size(); i++) {
assertTrue(sids.get(i) instanceof GrantedAuthoritySid);
}
for (int i = 1; i < sids.size(); i++) {
assertTrue(sids.get(i) instanceof GrantedAuthoritySid);
}
assertEquals("scott", ((PrincipalSid) sids.get(0)).getPrincipal());
assertEquals("A", ((GrantedAuthoritySid) sids.get(1)).getGrantedAuthority());
assertEquals("B", ((GrantedAuthoritySid) sids.get(2)).getGrantedAuthority());
assertEquals("C", ((GrantedAuthoritySid) sids.get(3)).getGrantedAuthority());
}
assertEquals("scott", ((PrincipalSid) sids.get(0)).getPrincipal());
assertEquals("A", ((GrantedAuthoritySid) sids.get(1)).getGrantedAuthority());
assertEquals("B", ((GrantedAuthoritySid) sids.get(2)).getGrantedAuthority());
assertEquals("C", ((GrantedAuthoritySid) sids.get(3)).getGrantedAuthority());
}
@Test
public void roleHierarchyIsUsedWhenSet() throws Exception {
RoleHierarchy rh = mock(RoleHierarchy.class);
List rhAuthorities = AuthorityUtils.createAuthorityList("D");
when(rh.getReachableGrantedAuthorities(anyCollection())).thenReturn(rhAuthorities);
SidRetrievalStrategy strat = new SidRetrievalStrategyImpl(rh);
@Test
public void roleHierarchyIsUsedWhenSet() throws Exception {
RoleHierarchy rh = mock(RoleHierarchy.class);
List rhAuthorities = AuthorityUtils.createAuthorityList("D");
when(rh.getReachableGrantedAuthorities(anyCollection()))
.thenReturn(rhAuthorities);
SidRetrievalStrategy strat = new SidRetrievalStrategyImpl(rh);
List<Sid> sids = strat.getSids(authentication);
assertEquals(2, sids.size());
assertNotNull(sids.get(0));
assertTrue(sids.get(0) instanceof PrincipalSid);
assertEquals("D", ((GrantedAuthoritySid) sids.get(1)).getGrantedAuthority());
}
List<Sid> sids = strat.getSids(authentication);
assertEquals(2, sids.size());
assertNotNull(sids.get(0));
assertTrue(sids.get(0) instanceof PrincipalSid);
assertEquals("D", ((GrantedAuthoritySid) sids.get(1)).getGrantedAuthority());
}
}

View File

@ -12,178 +12,193 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority;
public class SidTests extends TestCase {
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public void testPrincipalSidConstructorsRequiredFields() throws Exception {
// Check one String-argument constructor
try {
String string = null;
new PrincipalSid(string);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
public void testPrincipalSidConstructorsRequiredFields() throws Exception {
// Check one String-argument constructor
try {
String string = null;
new PrincipalSid(string);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
new PrincipalSid("");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
new PrincipalSid("");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
new PrincipalSid("johndoe");
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
try {
new PrincipalSid("johndoe");
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
// Check one Authentication-argument constructor
try {
Authentication authentication = null;
new PrincipalSid(authentication);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
// Check one Authentication-argument constructor
try {
Authentication authentication = null;
new PrincipalSid(authentication);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
Authentication authentication = new TestingAuthenticationToken(null, "password");
new PrincipalSid(authentication);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
Authentication authentication = new TestingAuthenticationToken(null,
"password");
new PrincipalSid(authentication);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
Authentication authentication = new TestingAuthenticationToken("johndoe", "password");
new PrincipalSid(authentication);
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
}
try {
Authentication authentication = new TestingAuthenticationToken("johndoe",
"password");
new PrincipalSid(authentication);
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
}
public void testGrantedAuthoritySidConstructorsRequiredFields() throws Exception {
// Check one String-argument constructor
try {
String string = null;
new GrantedAuthoritySid(string);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
public void testGrantedAuthoritySidConstructorsRequiredFields() throws Exception {
// Check one String-argument constructor
try {
String string = null;
new GrantedAuthoritySid(string);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
new GrantedAuthoritySid("");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
new GrantedAuthoritySid("");
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
new GrantedAuthoritySid("ROLE_TEST");
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
try {
new GrantedAuthoritySid("ROLE_TEST");
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
// Check one GrantedAuthority-argument constructor
try {
GrantedAuthority ga = null;
new GrantedAuthoritySid(ga);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
// Check one GrantedAuthority-argument constructor
try {
GrantedAuthority ga = null;
new GrantedAuthoritySid(ga);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
GrantedAuthority ga = new SimpleGrantedAuthority(null);
new GrantedAuthoritySid(ga);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
GrantedAuthority ga = new SimpleGrantedAuthority(null);
new GrantedAuthoritySid(ga);
Assert.fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
Assert.assertTrue(true);
}
try {
GrantedAuthority ga = new SimpleGrantedAuthority("ROLE_TEST");
new GrantedAuthoritySid(ga);
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
}
try {
GrantedAuthority ga = new SimpleGrantedAuthority("ROLE_TEST");
new GrantedAuthoritySid(ga);
Assert.assertTrue(true);
}
catch (IllegalArgumentException notExpected) {
Assert.fail("It shouldn't have thrown IllegalArgumentException");
}
}
public void testPrincipalSidEquals() throws Exception {
Authentication authentication = new TestingAuthenticationToken("johndoe", "password");
Sid principalSid = new PrincipalSid(authentication);
public void testPrincipalSidEquals() throws Exception {
Authentication authentication = new TestingAuthenticationToken("johndoe",
"password");
Sid principalSid = new PrincipalSid(authentication);
Assert.assertFalse(principalSid.equals(null));
Assert.assertFalse(principalSid.equals("DIFFERENT_TYPE_OBJECT"));
Assert.assertTrue(principalSid.equals(principalSid));
Assert.assertTrue(principalSid.equals(new PrincipalSid(authentication)));
Assert.assertTrue(principalSid.equals(new PrincipalSid(new TestingAuthenticationToken("johndoe", null))));
Assert.assertFalse(principalSid.equals(new PrincipalSid(new TestingAuthenticationToken("scott", null))));
Assert.assertTrue(principalSid.equals(new PrincipalSid("johndoe")));
Assert.assertFalse(principalSid.equals(new PrincipalSid("scott")));
}
Assert.assertFalse(principalSid.equals(null));
Assert.assertFalse(principalSid.equals("DIFFERENT_TYPE_OBJECT"));
Assert.assertTrue(principalSid.equals(principalSid));
Assert.assertTrue(principalSid.equals(new PrincipalSid(authentication)));
Assert.assertTrue(principalSid.equals(new PrincipalSid(
new TestingAuthenticationToken("johndoe", null))));
Assert.assertFalse(principalSid.equals(new PrincipalSid(
new TestingAuthenticationToken("scott", null))));
Assert.assertTrue(principalSid.equals(new PrincipalSid("johndoe")));
Assert.assertFalse(principalSid.equals(new PrincipalSid("scott")));
}
public void testGrantedAuthoritySidEquals() throws Exception {
GrantedAuthority ga = new SimpleGrantedAuthority("ROLE_TEST");
Sid gaSid = new GrantedAuthoritySid(ga);
public void testGrantedAuthoritySidEquals() throws Exception {
GrantedAuthority ga = new SimpleGrantedAuthority("ROLE_TEST");
Sid gaSid = new GrantedAuthoritySid(ga);
Assert.assertFalse(gaSid.equals(null));
Assert.assertFalse(gaSid.equals("DIFFERENT_TYPE_OBJECT"));
Assert.assertTrue(gaSid.equals(gaSid));
Assert.assertTrue(gaSid.equals(new GrantedAuthoritySid(ga)));
Assert.assertTrue(gaSid.equals(new GrantedAuthoritySid(new SimpleGrantedAuthority("ROLE_TEST"))));
Assert.assertFalse(gaSid.equals(new GrantedAuthoritySid(new SimpleGrantedAuthority("ROLE_NOT_EQUAL"))));
Assert.assertTrue(gaSid.equals(new GrantedAuthoritySid("ROLE_TEST")));
Assert.assertFalse(gaSid.equals(new GrantedAuthoritySid("ROLE_NOT_EQUAL")));
}
Assert.assertFalse(gaSid.equals(null));
Assert.assertFalse(gaSid.equals("DIFFERENT_TYPE_OBJECT"));
Assert.assertTrue(gaSid.equals(gaSid));
Assert.assertTrue(gaSid.equals(new GrantedAuthoritySid(ga)));
Assert.assertTrue(gaSid.equals(new GrantedAuthoritySid(
new SimpleGrantedAuthority("ROLE_TEST"))));
Assert.assertFalse(gaSid.equals(new GrantedAuthoritySid(
new SimpleGrantedAuthority("ROLE_NOT_EQUAL"))));
Assert.assertTrue(gaSid.equals(new GrantedAuthoritySid("ROLE_TEST")));
Assert.assertFalse(gaSid.equals(new GrantedAuthoritySid("ROLE_NOT_EQUAL")));
}
public void testPrincipalSidHashCode() throws Exception {
Authentication authentication = new TestingAuthenticationToken("johndoe", "password");
Sid principalSid = new PrincipalSid(authentication);
public void testPrincipalSidHashCode() throws Exception {
Authentication authentication = new TestingAuthenticationToken("johndoe",
"password");
Sid principalSid = new PrincipalSid(authentication);
Assert.assertTrue(principalSid.hashCode() == "johndoe".hashCode());
Assert.assertTrue(principalSid.hashCode() == new PrincipalSid("johndoe").hashCode());
Assert.assertTrue(principalSid.hashCode() != new PrincipalSid("scott").hashCode());
Assert.assertTrue(principalSid.hashCode() != new PrincipalSid(new TestingAuthenticationToken("scott", "password")).hashCode());
}
Assert.assertTrue(principalSid.hashCode() == "johndoe".hashCode());
Assert.assertTrue(principalSid.hashCode() == new PrincipalSid("johndoe")
.hashCode());
Assert.assertTrue(principalSid.hashCode() != new PrincipalSid("scott").hashCode());
Assert.assertTrue(principalSid.hashCode() != new PrincipalSid(
new TestingAuthenticationToken("scott", "password")).hashCode());
}
public void testGrantedAuthoritySidHashCode() throws Exception {
GrantedAuthority ga = new SimpleGrantedAuthority("ROLE_TEST");
Sid gaSid = new GrantedAuthoritySid(ga);
public void testGrantedAuthoritySidHashCode() throws Exception {
GrantedAuthority ga = new SimpleGrantedAuthority("ROLE_TEST");
Sid gaSid = new GrantedAuthoritySid(ga);
Assert.assertTrue(gaSid.hashCode() == "ROLE_TEST".hashCode());
Assert.assertTrue(gaSid.hashCode() == new GrantedAuthoritySid("ROLE_TEST").hashCode());
Assert.assertTrue(gaSid.hashCode() != new GrantedAuthoritySid("ROLE_TEST_2").hashCode());
Assert.assertTrue(gaSid.hashCode() != new GrantedAuthoritySid(new SimpleGrantedAuthority("ROLE_TEST_2")).hashCode());
}
Assert.assertTrue(gaSid.hashCode() == "ROLE_TEST".hashCode());
Assert.assertTrue(gaSid.hashCode() == new GrantedAuthoritySid("ROLE_TEST")
.hashCode());
Assert.assertTrue(gaSid.hashCode() != new GrantedAuthoritySid("ROLE_TEST_2")
.hashCode());
Assert.assertTrue(gaSid.hashCode() != new GrantedAuthoritySid(
new SimpleGrantedAuthority("ROLE_TEST_2")).hashCode());
}
public void testGetters() throws Exception {
Authentication authentication = new TestingAuthenticationToken("johndoe", "password");
PrincipalSid principalSid = new PrincipalSid(authentication);
GrantedAuthority ga = new SimpleGrantedAuthority("ROLE_TEST");
GrantedAuthoritySid gaSid = new GrantedAuthoritySid(ga);
public void testGetters() throws Exception {
Authentication authentication = new TestingAuthenticationToken("johndoe",
"password");
PrincipalSid principalSid = new PrincipalSid(authentication);
GrantedAuthority ga = new SimpleGrantedAuthority("ROLE_TEST");
GrantedAuthoritySid gaSid = new GrantedAuthoritySid(ga);
Assert.assertTrue("johndoe".equals(principalSid.getPrincipal()));
Assert.assertFalse("scott".equals(principalSid.getPrincipal()));
Assert.assertTrue("johndoe".equals(principalSid.getPrincipal()));
Assert.assertFalse("scott".equals(principalSid.getPrincipal()));
Assert.assertTrue("ROLE_TEST".equals(gaSid.getGrantedAuthority()));
Assert.assertFalse("ROLE_TEST2".equals(gaSid.getGrantedAuthority()));
}
Assert.assertTrue("ROLE_TEST".equals(gaSid.getGrantedAuthority()));
Assert.assertFalse("ROLE_TEST2".equals(gaSid.getGrantedAuthority()));
}
}

View File

@ -40,150 +40,160 @@ import org.springframework.security.core.context.SecurityContextHolder;
* @since 3.0.3
*/
public class AnnotationSecurityAspectTests {
private AffirmativeBased adm;
private @Mock AuthenticationManager authman;
private TestingAuthenticationToken anne = new TestingAuthenticationToken("anne", "", "ROLE_A");
// private TestingAuthenticationToken bob = new TestingAuthenticationToken("bob", "", "ROLE_B");
private AspectJMethodSecurityInterceptor interceptor;
private SecuredImpl secured = new SecuredImpl();
private SecuredImplSubclass securedSub = new SecuredImplSubclass();
private PrePostSecured prePostSecured = new PrePostSecured();
private AffirmativeBased adm;
private @Mock AuthenticationManager authman;
private TestingAuthenticationToken anne = new TestingAuthenticationToken("anne", "",
"ROLE_A");
// private TestingAuthenticationToken bob = new TestingAuthenticationToken("bob", "",
// "ROLE_B");
private AspectJMethodSecurityInterceptor interceptor;
private SecuredImpl secured = new SecuredImpl();
private SecuredImplSubclass securedSub = new SecuredImplSubclass();
private PrePostSecured prePostSecured = new PrePostSecured();
@Before
public final void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
interceptor = new AspectJMethodSecurityInterceptor();
AccessDecisionVoter[] voters = new AccessDecisionVoter[]
{new RoleVoter(), new PreInvocationAuthorizationAdviceVoter(new ExpressionBasedPreInvocationAdvice())};
adm = new AffirmativeBased(Arrays.<AccessDecisionVoter<? extends Object>>asList(voters));
interceptor.setAccessDecisionManager(adm);
interceptor.setAuthenticationManager(authman);
interceptor.setSecurityMetadataSource(new SecuredAnnotationSecurityMetadataSource());
AnnotationSecurityAspect secAspect = AnnotationSecurityAspect.aspectOf();
secAspect.setSecurityInterceptor(interceptor);
}
@Before
public final void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
interceptor = new AspectJMethodSecurityInterceptor();
AccessDecisionVoter[] voters = new AccessDecisionVoter[] {
new RoleVoter(),
new PreInvocationAuthorizationAdviceVoter(
new ExpressionBasedPreInvocationAdvice()) };
adm = new AffirmativeBased(
Arrays.<AccessDecisionVoter<? extends Object>> asList(voters));
interceptor.setAccessDecisionManager(adm);
interceptor.setAuthenticationManager(authman);
interceptor
.setSecurityMetadataSource(new SecuredAnnotationSecurityMetadataSource());
AnnotationSecurityAspect secAspect = AnnotationSecurityAspect.aspectOf();
secAspect.setSecurityInterceptor(interceptor);
}
@After
public void clearContext() {
SecurityContextHolder.clearContext();
}
@After
public void clearContext() {
SecurityContextHolder.clearContext();
}
@Test
public void securedInterfaceMethodAllowsAllAccess() throws Exception {
secured.securedMethod();
}
@Test
public void securedInterfaceMethodAllowsAllAccess() throws Exception {
secured.securedMethod();
}
@Test(expected=AuthenticationCredentialsNotFoundException.class)
public void securedClassMethodDeniesUnauthenticatedAccess() throws Exception {
secured.securedClassMethod();
}
@Test(expected = AuthenticationCredentialsNotFoundException.class)
public void securedClassMethodDeniesUnauthenticatedAccess() throws Exception {
secured.securedClassMethod();
}
@Test
public void securedClassMethodAllowsAccessToRoleA() throws Exception {
SecurityContextHolder.getContext().setAuthentication(anne);
secured.securedClassMethod();
}
@Test
public void securedClassMethodAllowsAccessToRoleA() throws Exception {
SecurityContextHolder.getContext().setAuthentication(anne);
secured.securedClassMethod();
}
@Test(expected=AccessDeniedException.class)
public void internalPrivateCallIsIntercepted() {
SecurityContextHolder.getContext().setAuthentication(anne);
@Test(expected = AccessDeniedException.class)
public void internalPrivateCallIsIntercepted() {
SecurityContextHolder.getContext().setAuthentication(anne);
try {
secured.publicCallsPrivate();
fail("Expected AccessDeniedException");
} catch (AccessDeniedException expected) {
}
securedSub.publicCallsPrivate();
}
try {
secured.publicCallsPrivate();
fail("Expected AccessDeniedException");
}
catch (AccessDeniedException expected) {
}
securedSub.publicCallsPrivate();
}
@Test(expected=AccessDeniedException.class)
public void protectedMethodIsIntercepted() throws Exception {
SecurityContextHolder.getContext().setAuthentication(anne);
@Test(expected = AccessDeniedException.class)
public void protectedMethodIsIntercepted() throws Exception {
SecurityContextHolder.getContext().setAuthentication(anne);
secured.protectedMethod();
}
secured.protectedMethod();
}
@Test
public void overriddenProtectedMethodIsNotIntercepted() throws Exception {
// AspectJ doesn't inherit annotations
securedSub.protectedMethod();
}
@Test
public void overriddenProtectedMethodIsNotIntercepted() throws Exception {
// AspectJ doesn't inherit annotations
securedSub.protectedMethod();
}
// SEC-1262
@Test(expected=AccessDeniedException.class)
public void denyAllPreAuthorizeDeniesAccess() throws Exception {
configureForElAnnotations();
SecurityContextHolder.getContext().setAuthentication(anne);
prePostSecured.denyAllMethod();
}
// SEC-1262
@Test(expected = AccessDeniedException.class)
public void denyAllPreAuthorizeDeniesAccess() throws Exception {
configureForElAnnotations();
SecurityContextHolder.getContext().setAuthentication(anne);
prePostSecured.denyAllMethod();
}
@Test
public void postFilterIsApplied() throws Exception {
configureForElAnnotations();
SecurityContextHolder.getContext().setAuthentication(anne);
List<String> objects = prePostSecured.postFilterMethod();
assertEquals(2, objects.size());
assertTrue(objects.contains("apple"));
assertTrue(objects.contains("aubergine"));
}
@Test
public void postFilterIsApplied() throws Exception {
configureForElAnnotations();
SecurityContextHolder.getContext().setAuthentication(anne);
List<String> objects = prePostSecured.postFilterMethod();
assertEquals(2, objects.size());
assertTrue(objects.contains("apple"));
assertTrue(objects.contains("aubergine"));
}
private void configureForElAnnotations() {
DefaultMethodSecurityExpressionHandler eh = new DefaultMethodSecurityExpressionHandler();
interceptor.setSecurityMetadataSource(new PrePostAnnotationSecurityMetadataSource(
new ExpressionBasedAnnotationAttributeFactory(eh)));
interceptor.setAccessDecisionManager(adm);
AfterInvocationProviderManager aim = new AfterInvocationProviderManager();
aim.setProviders(Arrays.asList(new PostInvocationAdviceProvider(new ExpressionBasedPostInvocationAdvice(eh))));
interceptor.setAfterInvocationManager(aim);
}
private void configureForElAnnotations() {
DefaultMethodSecurityExpressionHandler eh = new DefaultMethodSecurityExpressionHandler();
interceptor
.setSecurityMetadataSource(new PrePostAnnotationSecurityMetadataSource(
new ExpressionBasedAnnotationAttributeFactory(eh)));
interceptor.setAccessDecisionManager(adm);
AfterInvocationProviderManager aim = new AfterInvocationProviderManager();
aim.setProviders(Arrays.asList(new PostInvocationAdviceProvider(
new ExpressionBasedPostInvocationAdvice(eh))));
interceptor.setAfterInvocationManager(aim);
}
}
interface SecuredInterface {
@Secured("ROLE_X")
void securedMethod();
@Secured("ROLE_X")
void securedMethod();
}
class SecuredImpl implements SecuredInterface {
// Not really secured because AspectJ doesn't inherit annotations from interfaces
public void securedMethod() {
}
// Not really secured because AspectJ doesn't inherit annotations from interfaces
public void securedMethod() {
}
@Secured("ROLE_A")
public void securedClassMethod() {
}
@Secured("ROLE_A")
public void securedClassMethod() {
}
@Secured("ROLE_X")
private void privateMethod() {
}
@Secured("ROLE_X")
private void privateMethod() {
}
@Secured("ROLE_X")
protected void protectedMethod() {
}
@Secured("ROLE_X")
protected void protectedMethod() {
}
@Secured("ROLE_X")
public void publicCallsPrivate() {
privateMethod();
}
@Secured("ROLE_X")
public void publicCallsPrivate() {
privateMethod();
}
}
class SecuredImplSubclass extends SecuredImpl {
protected void protectedMethod() {
}
protected void protectedMethod() {
}
public void publicCallsPrivate() {
super.publicCallsPrivate();
}
public void publicCallsPrivate() {
super.publicCallsPrivate();
}
}
class PrePostSecured {
@PreAuthorize("denyAll")
public void denyAllMethod() {
}
@PreAuthorize("denyAll")
public void denyAllMethod() {
}
@PostFilter("filterObject.startsWith('a')")
public List<String> postFilterMethod() {
ArrayList<String> objects = new ArrayList<String>();
objects.addAll(Arrays.asList(new String[] {"apple", "banana", "aubergine", "orange"}));
return objects;
}
@PostFilter("filterObject.startsWith('a')")
public List<String> postFilterMethod() {
ArrayList<String> objects = new ArrayList<String>();
objects.addAll(Arrays.asList(new String[] { "apple", "banana", "aubergine",
"orange" }));
return objects;
}
}

View File

@ -15,19 +15,20 @@
package org.springframework.security.cas;
/**
* Sets the appropriate parameters for CAS's implementation of SAML (which is not guaranteed to be actually SAML compliant).
* Sets the appropriate parameters for CAS's implementation of SAML (which is not
* guaranteed to be actually SAML compliant).
*
* @author Scott Battaglia
* @since 3.0
*/
public final class SamlServiceProperties extends ServiceProperties {
public static final String DEFAULT_SAML_ARTIFACT_PARAMETER = "SAMLart";
public static final String DEFAULT_SAML_ARTIFACT_PARAMETER = "SAMLart";
public static final String DEFAULT_SAML_SERVICE_PARAMETER = "TARGET";
public static final String DEFAULT_SAML_SERVICE_PARAMETER = "TARGET";
public SamlServiceProperties() {
super.setArtifactParameter(DEFAULT_SAML_ARTIFACT_PARAMETER);
super.setServiceParameter(DEFAULT_SAML_SERVICE_PARAMETER);
}
public SamlServiceProperties() {
super.setArtifactParameter(DEFAULT_SAML_ARTIFACT_PARAMETER);
super.setServiceParameter(DEFAULT_SAML_SERVICE_PARAMETER);
}
}

View File

@ -18,118 +18,123 @@ package org.springframework.security.cas;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* Stores properties related to this CAS service.
* <p>
* Each web application capable of processing CAS tickets is known as a service.
* This class stores the properties that are relevant to the local CAS service, being the application
* that is being secured by Spring Security.
* Each web application capable of processing CAS tickets is known as a service. This
* class stores the properties that are relevant to the local CAS service, being the
* application that is being secured by Spring Security.
*
* @author Ben Alex
*/
public class ServiceProperties implements InitializingBean {
public static final String DEFAULT_CAS_ARTIFACT_PARAMETER = "ticket";
public static final String DEFAULT_CAS_ARTIFACT_PARAMETER = "ticket";
public static final String DEFAULT_CAS_SERVICE_PARAMETER = "service";
public static final String DEFAULT_CAS_SERVICE_PARAMETER = "service";
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private String service;
private String service;
private boolean authenticateAllArtifacts;
private boolean authenticateAllArtifacts;
private boolean sendRenew = false;
private boolean sendRenew = false;
private String artifactParameter = DEFAULT_CAS_ARTIFACT_PARAMETER;
private String artifactParameter = DEFAULT_CAS_ARTIFACT_PARAMETER;
private String serviceParameter = DEFAULT_CAS_SERVICE_PARAMETER;
private String serviceParameter = DEFAULT_CAS_SERVICE_PARAMETER;
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.hasLength(this.service, "service cannot be empty.");
Assert.hasLength(this.artifactParameter, "artifactParameter cannot be empty.");
Assert.hasLength(this.serviceParameter, "serviceParameter cannot be empty.");
}
public void afterPropertiesSet() throws Exception {
Assert.hasLength(this.service, "service cannot be empty.");
Assert.hasLength(this.artifactParameter, "artifactParameter cannot be empty.");
Assert.hasLength(this.serviceParameter, "serviceParameter cannot be empty.");
}
/**
* Represents the service the user is authenticating to.
* <p>
* This service is the callback URL belonging to the local Spring Security System for Spring secured application.
* For example,
* <pre>
* https://www.mycompany.com/application/login/cas
* </pre>
*
* @return the URL of the service the user is authenticating to
*/
public final String getService() {
return this.service;
}
/**
* Represents the service the user is authenticating to.
* <p>
* This service is the callback URL belonging to the local Spring Security System for
* Spring secured application. For example,
*
* <pre>
* https://www.mycompany.com/application/login/cas
* </pre>
*
* @return the URL of the service the user is authenticating to
*/
public final String getService() {
return this.service;
}
/**
* Indicates whether the <code>renew</code> parameter should be sent to the CAS login URL and CAS
* validation URL.
* <p>
* If <code>true</code>, it will force CAS to authenticate the user again (even if the
* user has previously authenticated). During ticket validation it will require the ticket was generated as a
* consequence of an explicit login. High security applications would probably set this to <code>true</code>.
* Defaults to <code>false</code>, providing automated single sign on.
*
* @return whether to send the <code>renew</code> parameter to CAS
*/
public final boolean isSendRenew() {
return this.sendRenew;
}
/**
* Indicates whether the <code>renew</code> parameter should be sent to the CAS login
* URL and CAS validation URL.
* <p>
* If <code>true</code>, it will force CAS to authenticate the user again (even if the
* user has previously authenticated). During ticket validation it will require the
* ticket was generated as a consequence of an explicit login. High security
* applications would probably set this to <code>true</code>. Defaults to
* <code>false</code>, providing automated single sign on.
*
* @return whether to send the <code>renew</code> parameter to CAS
*/
public final boolean isSendRenew() {
return this.sendRenew;
}
public final void setSendRenew(final boolean sendRenew) {
this.sendRenew = sendRenew;
}
public final void setSendRenew(final boolean sendRenew) {
this.sendRenew = sendRenew;
}
public final void setService(final String service) {
this.service = service;
}
public final void setService(final String service) {
this.service = service;
}
public final String getArtifactParameter() {
return this.artifactParameter;
}
public final String getArtifactParameter() {
return this.artifactParameter;
}
/**
* Configures the Request Parameter to look for when attempting to see if a CAS ticket was sent from the server.
*
* @param artifactParameter the id to use. Default is "ticket".
*/
public final void setArtifactParameter(final String artifactParameter) {
this.artifactParameter = artifactParameter;
}
/**
* Configures the Request Parameter to look for when attempting to see if a CAS ticket
* was sent from the server.
*
* @param artifactParameter the id to use. Default is "ticket".
*/
public final void setArtifactParameter(final String artifactParameter) {
this.artifactParameter = artifactParameter;
}
/**
* Configures the Request parameter to look for when attempting to send a request to CAS.
*
* @return the service parameter to use. Default is "service".
*/
public final String getServiceParameter() {
return serviceParameter;
}
/**
* Configures the Request parameter to look for when attempting to send a request to
* CAS.
*
* @return the service parameter to use. Default is "service".
*/
public final String getServiceParameter() {
return serviceParameter;
}
public final void setServiceParameter(final String serviceParameter) {
this.serviceParameter = serviceParameter;
}
public final void setServiceParameter(final String serviceParameter) {
this.serviceParameter = serviceParameter;
}
public final boolean isAuthenticateAllArtifacts() {
return authenticateAllArtifacts;
}
public final boolean isAuthenticateAllArtifacts() {
return authenticateAllArtifacts;
}
/**
* If true, then any non-null artifact (ticket) should be authenticated.
* Additionally, the service will be determined dynamically in order to
* ensure the service matches the expected value for this artifact.
*
* @param authenticateAllArtifacts
*/
public final void setAuthenticateAllArtifacts(final boolean authenticateAllArtifacts) {
this.authenticateAllArtifacts = authenticateAllArtifacts;
}
/**
* If true, then any non-null artifact (ticket) should be authenticated. Additionally,
* the service will be determined dynamically in order to ensure the service matches
* the expected value for this artifact.
*
* @param authenticateAllArtifacts
*/
public final void setAuthenticateAllArtifacts(final boolean authenticateAllArtifacts) {
this.authenticateAllArtifacts = authenticateAllArtifacts;
}
}

View File

@ -29,28 +29,28 @@ import org.springframework.security.core.SpringSecurityCoreVersion;
*/
public final class CasAssertionAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final Assertion assertion;
private final Assertion assertion;
private final String ticket;
private final String ticket;
public CasAssertionAuthenticationToken(final Assertion assertion, final String ticket) {
super(new ArrayList<GrantedAuthority>());
public CasAssertionAuthenticationToken(final Assertion assertion, final String ticket) {
super(new ArrayList<GrantedAuthority>());
this.assertion = assertion;
this.ticket = ticket;
}
this.assertion = assertion;
this.ticket = ticket;
}
public Object getPrincipal() {
return this.assertion.getPrincipal().getName();
}
public Object getPrincipal() {
return this.assertion.getPrincipal().getName();
}
public Object getCredentials() {
return this.ticket;
}
public Object getCredentials() {
return this.ticket;
}
public Assertion getAssertion() {
return this.assertion;
}
public Assertion getAssertion() {
return this.assertion;
}
}

View File

@ -39,204 +39,236 @@ import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper
import org.springframework.security.core.userdetails.*;
import org.springframework.util.Assert;
/**
* An {@link AuthenticationProvider} implementation that integrates with JA-SIG Central Authentication Service
* (CAS).
* An {@link AuthenticationProvider} implementation that integrates with JA-SIG Central
* Authentication Service (CAS).
* <p>
* This <code>AuthenticationProvider</code> is capable of validating {@link UsernamePasswordAuthenticationToken}
* requests which contain a <code>principal</code> name equal to either
* {@link CasAuthenticationFilter#CAS_STATEFUL_IDENTIFIER} or {@link CasAuthenticationFilter#CAS_STATELESS_IDENTIFIER}.
* It can also validate a previously created {@link CasAuthenticationToken}.
* This <code>AuthenticationProvider</code> is capable of validating
* {@link UsernamePasswordAuthenticationToken} requests which contain a
* <code>principal</code> name equal to either
* {@link CasAuthenticationFilter#CAS_STATEFUL_IDENTIFIER} or
* {@link CasAuthenticationFilter#CAS_STATELESS_IDENTIFIER}. It can also validate a
* previously created {@link CasAuthenticationToken}.
*
* @author Ben Alex
* @author Scott Battaglia
*/
public class CasAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
//~ Static fields/initializers =====================================================================================
public class CasAuthenticationProvider implements AuthenticationProvider,
InitializingBean, MessageSourceAware {
// ~ Static fields/initializers
// =====================================================================================
private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);
private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService;
private AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService;
private final UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private StatelessTicketCache statelessTicketCache = new NullStatelessTicketCache();
private String key;
private TicketValidator ticketValidator;
private ServiceProperties serviceProperties;
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
private final UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private StatelessTicketCache statelessTicketCache = new NullStatelessTicketCache();
private String key;
private TicketValidator ticketValidator;
private ServiceProperties serviceProperties;
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
// ~ Methods
// ========================================================================================================
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.authenticationUserDetailsService,
"An authenticationUserDetailsService must be set");
Assert.notNull(this.ticketValidator, "A ticketValidator must be set");
Assert.notNull(this.statelessTicketCache, "A statelessTicketCache must be set");
Assert.hasText(
this.key,
"A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated");
Assert.notNull(this.messages, "A message source must be set");
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.authenticationUserDetailsService, "An authenticationUserDetailsService must be set");
Assert.notNull(this.ticketValidator, "A ticketValidator must be set");
Assert.notNull(this.statelessTicketCache, "A statelessTicketCache must be set");
Assert.hasText(this.key, "A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated");
Assert.notNull(this.messages, "A message source must be set");
}
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
if (!supports(authentication.getClass())) {
return null;
}
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (!supports(authentication.getClass())) {
return null;
}
if (authentication instanceof UsernamePasswordAuthenticationToken
&& (!CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER
.equals(authentication.getPrincipal().toString()) && !CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER
.equals(authentication.getPrincipal().toString()))) {
// UsernamePasswordAuthenticationToken not CAS related
return null;
}
if (authentication instanceof UsernamePasswordAuthenticationToken
&& (!CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER.equals(authentication.getPrincipal().toString())
&& !CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER.equals(authentication.getPrincipal().toString()))) {
// UsernamePasswordAuthenticationToken not CAS related
return null;
}
// If an existing CasAuthenticationToken, just check we created it
if (authentication instanceof CasAuthenticationToken) {
if (this.key.hashCode() == ((CasAuthenticationToken) authentication)
.getKeyHash()) {
return authentication;
}
else {
throw new BadCredentialsException(
messages.getMessage("CasAuthenticationProvider.incorrectKey",
"The presented CasAuthenticationToken does not contain the expected key"));
}
}
// If an existing CasAuthenticationToken, just check we created it
if (authentication instanceof CasAuthenticationToken) {
if (this.key.hashCode() == ((CasAuthenticationToken) authentication).getKeyHash()) {
return authentication;
} else {
throw new BadCredentialsException(messages.getMessage("CasAuthenticationProvider.incorrectKey",
"The presented CasAuthenticationToken does not contain the expected key"));
}
}
// Ensure credentials are presented
if ((authentication.getCredentials() == null)
|| "".equals(authentication.getCredentials())) {
throw new BadCredentialsException(messages.getMessage(
"CasAuthenticationProvider.noServiceTicket",
"Failed to provide a CAS service ticket to validate"));
}
// Ensure credentials are presented
if ((authentication.getCredentials() == null) || "".equals(authentication.getCredentials())) {
throw new BadCredentialsException(messages.getMessage("CasAuthenticationProvider.noServiceTicket",
"Failed to provide a CAS service ticket to validate"));
}
boolean stateless = false;
boolean stateless = false;
if (authentication instanceof UsernamePasswordAuthenticationToken
&& CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER.equals(authentication
.getPrincipal())) {
stateless = true;
}
if (authentication instanceof UsernamePasswordAuthenticationToken
&& CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER.equals(authentication.getPrincipal())) {
stateless = true;
}
CasAuthenticationToken result = null;
CasAuthenticationToken result = null;
if (stateless) {
// Try to obtain from cache
result = statelessTicketCache.getByTicketId(authentication.getCredentials()
.toString());
}
if (stateless) {
// Try to obtain from cache
result = statelessTicketCache.getByTicketId(authentication.getCredentials().toString());
}
if (result == null) {
result = this.authenticateNow(authentication);
result.setDetails(authentication.getDetails());
}
if (result == null) {
result = this.authenticateNow(authentication);
result.setDetails(authentication.getDetails());
}
if (stateless) {
// Add to cache
statelessTicketCache.putTicketInCache(result);
}
if (stateless) {
// Add to cache
statelessTicketCache.putTicketInCache(result);
}
return result;
}
return result;
}
private CasAuthenticationToken authenticateNow(final Authentication authentication)
throws AuthenticationException {
try {
final Assertion assertion = this.ticketValidator.validate(authentication
.getCredentials().toString(), getServiceUrl(authentication));
final UserDetails userDetails = loadUserByAssertion(assertion);
userDetailsChecker.check(userDetails);
return new CasAuthenticationToken(this.key, userDetails,
authentication.getCredentials(),
authoritiesMapper.mapAuthorities(userDetails.getAuthorities()),
userDetails, assertion);
}
catch (final TicketValidationException e) {
throw new BadCredentialsException(e.getMessage(), e);
}
}
private CasAuthenticationToken authenticateNow(final Authentication authentication) throws AuthenticationException {
try {
final Assertion assertion = this.ticketValidator.validate(authentication.getCredentials().toString(), getServiceUrl(authentication));
final UserDetails userDetails = loadUserByAssertion(assertion);
userDetailsChecker.check(userDetails);
return new CasAuthenticationToken(this.key, userDetails, authentication.getCredentials(),
authoritiesMapper.mapAuthorities(userDetails.getAuthorities()), userDetails, assertion);
} catch (final TicketValidationException e) {
throw new BadCredentialsException(e.getMessage(), e);
}
}
/**
* Gets the serviceUrl. If the {@link Authentication#getDetails()} is an instance of
* {@link ServiceAuthenticationDetails}, then
* {@link ServiceAuthenticationDetails#getServiceUrl()} is used. Otherwise, the
* {@link ServiceProperties#getService()} is used.
*
* @param authentication
* @return
*/
private String getServiceUrl(Authentication authentication) {
String serviceUrl;
if (authentication.getDetails() instanceof ServiceAuthenticationDetails) {
serviceUrl = ((ServiceAuthenticationDetails) authentication.getDetails())
.getServiceUrl();
}
else if (serviceProperties == null) {
throw new IllegalStateException(
"serviceProperties cannot be null unless Authentication.getDetails() implements ServiceAuthenticationDetails.");
}
else if (serviceProperties.getService() == null) {
throw new IllegalStateException(
"serviceProperties.getService() cannot be null unless Authentication.getDetails() implements ServiceAuthenticationDetails.");
}
else {
serviceUrl = serviceProperties.getService();
}
if (logger.isDebugEnabled()) {
logger.debug("serviceUrl = " + serviceUrl);
}
return serviceUrl;
}
/**
* Gets the serviceUrl. If the {@link Authentication#getDetails()} is an
* instance of {@link ServiceAuthenticationDetails}, then
* {@link ServiceAuthenticationDetails#getServiceUrl()} is used. Otherwise,
* the {@link ServiceProperties#getService()} is used.
*
* @param authentication
* @return
*/
private String getServiceUrl(Authentication authentication) {
String serviceUrl;
if(authentication.getDetails() instanceof ServiceAuthenticationDetails) {
serviceUrl = ((ServiceAuthenticationDetails)authentication.getDetails()).getServiceUrl();
}else if(serviceProperties == null){
throw new IllegalStateException("serviceProperties cannot be null unless Authentication.getDetails() implements ServiceAuthenticationDetails.");
}else if(serviceProperties.getService() == null){
throw new IllegalStateException("serviceProperties.getService() cannot be null unless Authentication.getDetails() implements ServiceAuthenticationDetails.");
}else {
serviceUrl = serviceProperties.getService();
}
if(logger.isDebugEnabled()) {
logger.debug("serviceUrl = "+serviceUrl);
}
return serviceUrl;
}
/**
* Template method for retrieving the UserDetails based on the assertion. Default is
* to call configured userDetailsService and pass the username. Deployers can override
* this method and retrieve the user based on any criteria they desire.
*
* @param assertion The CAS Assertion.
* @return the UserDetails.
*/
protected UserDetails loadUserByAssertion(final Assertion assertion) {
final CasAssertionAuthenticationToken token = new CasAssertionAuthenticationToken(
assertion, "");
return this.authenticationUserDetailsService.loadUserDetails(token);
}
/**
* Template method for retrieving the UserDetails based on the assertion. Default is to call configured userDetailsService and pass the username. Deployers
* can override this method and retrieve the user based on any criteria they desire.
*
* @param assertion The CAS Assertion.
* @return the UserDetails.
*/
protected UserDetails loadUserByAssertion(final Assertion assertion) {
final CasAssertionAuthenticationToken token = new CasAssertionAuthenticationToken(assertion, "");
return this.authenticationUserDetailsService.loadUserDetails(token);
}
@SuppressWarnings("unchecked")
/**
* Sets the UserDetailsService to use. This is a convenience method to invoke
*/
public void setUserDetailsService(final UserDetailsService userDetailsService) {
this.authenticationUserDetailsService = new UserDetailsByNameServiceWrapper(
userDetailsService);
}
@SuppressWarnings("unchecked")
/**
* Sets the UserDetailsService to use. This is a convenience method to invoke
*/
public void setUserDetailsService(final UserDetailsService userDetailsService) {
this.authenticationUserDetailsService = new UserDetailsByNameServiceWrapper(userDetailsService);
}
public void setAuthenticationUserDetailsService(
final AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService) {
this.authenticationUserDetailsService = authenticationUserDetailsService;
}
public void setServiceProperties(final ServiceProperties serviceProperties) {
this.serviceProperties = serviceProperties;
}
public void setAuthenticationUserDetailsService(final AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService) {
this.authenticationUserDetailsService = authenticationUserDetailsService;
}
protected String getKey() {
return key;
}
public void setServiceProperties(final ServiceProperties serviceProperties) {
this.serviceProperties = serviceProperties;
}
public void setKey(String key) {
this.key = key;
}
protected String getKey() {
return key;
}
public StatelessTicketCache getStatelessTicketCache() {
return statelessTicketCache;
}
public void setKey(String key) {
this.key = key;
}
protected TicketValidator getTicketValidator() {
return ticketValidator;
}
public StatelessTicketCache getStatelessTicketCache() {
return statelessTicketCache;
}
public void setMessageSource(final MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
protected TicketValidator getTicketValidator() {
return ticketValidator;
}
public void setStatelessTicketCache(final StatelessTicketCache statelessTicketCache) {
this.statelessTicketCache = statelessTicketCache;
}
public void setMessageSource(final MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
public void setTicketValidator(final TicketValidator ticketValidator) {
this.ticketValidator = ticketValidator;
}
public void setStatelessTicketCache(final StatelessTicketCache statelessTicketCache) {
this.statelessTicketCache = statelessTicketCache;
}
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
this.authoritiesMapper = authoritiesMapper;
}
public void setTicketValidator(final TicketValidator ticketValidator) {
this.ticketValidator = ticketValidator;
}
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
this.authoritiesMapper = authoritiesMapper;
}
public boolean supports(final Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)) ||
(CasAuthenticationToken.class.isAssignableFrom(authentication)) ||
(CasAssertionAuthenticationToken.class.isAssignableFrom(authentication));
}
public boolean supports(final Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class
.isAssignableFrom(authentication))
|| (CasAuthenticationToken.class.isAssignableFrom(authentication))
|| (CasAssertionAuthenticationToken.class
.isAssignableFrom(authentication));
}
}

View File

@ -30,103 +30,114 @@ import org.springframework.security.core.userdetails.UserDetails;
* @author Ben Alex
* @author Scott Battaglia
*/
public class CasAuthenticationToken extends AbstractAuthenticationToken implements Serializable {
public class CasAuthenticationToken extends AbstractAuthenticationToken implements
Serializable {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
//~ Instance fields ================================================================================================
private final Object credentials;
private final Object principal;
private final UserDetails userDetails;
private final int keyHash;
private final Assertion assertion;
// ~ Instance fields
// ================================================================================================
private final Object credentials;
private final Object principal;
private final UserDetails userDetails;
private final int keyHash;
private final Assertion assertion;
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
/**
* Constructor.
*
* @param key to identify if this object made by a given {@link
* CasAuthenticationProvider}
* @param principal typically the UserDetails object (cannot be <code>null</code>)
* @param credentials the service/proxy ticket ID from CAS (cannot be
* <code>null</code>)
* @param authorities the authorities granted to the user (from the {@link
* org.springframework.security.core.userdetails.UserDetailsService}) (cannot be <code>null</code>)
* @param userDetails the user details (from the {@link
* org.springframework.security.core.userdetails.UserDetailsService}) (cannot be <code>null</code>)
* @param assertion the assertion returned from the CAS servers. It contains the principal and how to obtain a
* proxy ticket for the user.
*
* @throws IllegalArgumentException if a <code>null</code> was passed
*/
public CasAuthenticationToken(final String key, final Object principal, final Object credentials,
final Collection<? extends GrantedAuthority> authorities, final UserDetails userDetails, final Assertion assertion) {
super(authorities);
/**
* Constructor.
*
* @param key to identify if this object made by a given
* {@link CasAuthenticationProvider}
* @param principal typically the UserDetails object (cannot be <code>null</code>)
* @param credentials the service/proxy ticket ID from CAS (cannot be
* <code>null</code>)
* @param authorities the authorities granted to the user (from the
* {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot
* be <code>null</code>)
* @param userDetails the user details (from the
* {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot
* be <code>null</code>)
* @param assertion the assertion returned from the CAS servers. It contains the
* principal and how to obtain a proxy ticket for the user.
*
* @throws IllegalArgumentException if a <code>null</code> was passed
*/
public CasAuthenticationToken(final String key, final Object principal,
final Object credentials,
final Collection<? extends GrantedAuthority> authorities,
final UserDetails userDetails, final Assertion assertion) {
super(authorities);
if ((key == null) || ("".equals(key)) || (principal == null) || "".equals(principal) || (credentials == null)
|| "".equals(credentials) || (authorities == null) || (userDetails == null) || (assertion == null)) {
throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
}
if ((key == null) || ("".equals(key)) || (principal == null)
|| "".equals(principal) || (credentials == null)
|| "".equals(credentials) || (authorities == null)
|| (userDetails == null) || (assertion == null)) {
throw new IllegalArgumentException(
"Cannot pass null or empty values to constructor");
}
this.keyHash = key.hashCode();
this.principal = principal;
this.credentials = credentials;
this.userDetails = userDetails;
this.assertion = assertion;
setAuthenticated(true);
}
this.keyHash = key.hashCode();
this.principal = principal;
this.credentials = credentials;
this.userDetails = userDetails;
this.assertion = assertion;
setAuthenticated(true);
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public boolean equals(final Object obj) {
if (!super.equals(obj)) {
return false;
}
public boolean equals(final Object obj) {
if (!super.equals(obj)) {
return false;
}
if (obj instanceof CasAuthenticationToken) {
CasAuthenticationToken test = (CasAuthenticationToken) obj;
if (obj instanceof CasAuthenticationToken) {
CasAuthenticationToken test = (CasAuthenticationToken) obj;
if (!this.assertion.equals(test.getAssertion())) {
return false;
}
if (!this.assertion.equals(test.getAssertion())) {
return false;
}
if (this.getKeyHash() != test.getKeyHash()) {
return false;
}
if (this.getKeyHash() != test.getKeyHash()) {
return false;
}
return true;
}
return true;
}
return false;
}
return false;
}
public Object getCredentials() {
return this.credentials;
}
public Object getCredentials() {
return this.credentials;
}
public int getKeyHash() {
return this.keyHash;
}
public int getKeyHash() {
return this.keyHash;
}
public Object getPrincipal() {
return this.principal;
}
public Object getPrincipal() {
return this.principal;
}
public Assertion getAssertion() {
return this.assertion;
}
public Assertion getAssertion() {
return this.assertion;
}
public UserDetails getUserDetails() {
return userDetails;
}
public UserDetails getUserDetails() {
return userDetails;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString());
sb.append(" Assertion: ").append(this.assertion);
sb.append(" Credentials (Service/Proxy Ticket): ").append(this.credentials);
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString());
sb.append(" Assertion: ").append(this.assertion);
sb.append(" Credentials (Service/Proxy Ticket): ").append(this.credentials);
return (sb.toString());
}
return (sb.toString());
}
}

View File

@ -23,64 +23,68 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* Caches tickets using a Spring IoC defined <a href="http://ehcache.sourceforge.net">EHCACHE</a>.
* Caches tickets using a Spring IoC defined <a
* href="http://ehcache.sourceforge.net">EHCACHE</a>.
*
* @author Ben Alex
*/
public class EhCacheBasedTicketCache implements StatelessTicketCache, InitializingBean {
//~ Static fields/initializers =====================================================================================
// ~ Static fields/initializers
// =====================================================================================
private static final Log logger = LogFactory.getLog(EhCacheBasedTicketCache.class);
private static final Log logger = LogFactory.getLog(EhCacheBasedTicketCache.class);
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private Ehcache cache;
private Ehcache cache;
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache, "cache mandatory");
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache, "cache mandatory");
}
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
final Element element = cache.get(serviceTicket);
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
final Element element = cache.get(serviceTicket);
if (logger.isDebugEnabled()) {
logger.debug("Cache hit: " + (element != null) + "; service ticket: " + serviceTicket);
}
if (logger.isDebugEnabled()) {
logger.debug("Cache hit: " + (element != null) + "; service ticket: "
+ serviceTicket);
}
return element == null ? null : (CasAuthenticationToken) element.getValue();
}
return element == null ? null : (CasAuthenticationToken) element.getValue();
}
public Ehcache getCache() {
return cache;
}
public Ehcache getCache() {
return cache;
}
public void putTicketInCache(final CasAuthenticationToken token) {
final Element element = new Element(token.getCredentials().toString(), token);
public void putTicketInCache(final CasAuthenticationToken token) {
final Element element = new Element(token.getCredentials().toString(), token);
if (logger.isDebugEnabled()) {
logger.debug("Cache put: " + element.getKey());
}
if (logger.isDebugEnabled()) {
logger.debug("Cache put: " + element.getKey());
}
cache.put(element);
}
cache.put(element);
}
public void removeTicketFromCache(final CasAuthenticationToken token) {
if (logger.isDebugEnabled()) {
logger.debug("Cache remove: " + token.getCredentials().toString());
}
public void removeTicketFromCache(final CasAuthenticationToken token) {
if (logger.isDebugEnabled()) {
logger.debug("Cache remove: " + token.getCredentials().toString());
}
this.removeTicketFromCache(token.getCredentials().toString());
}
this.removeTicketFromCache(token.getCredentials().toString());
}
public void removeTicketFromCache(final String serviceTicket) {
cache.remove(serviceTicket);
}
public void removeTicketFromCache(final String serviceTicket) {
cache.remove(serviceTicket);
}
public void setCache(final Ehcache cache) {
this.cache = cache;
}
public void setCache(final Ehcache cache) {
this.cache = cache;
}
}

View File

@ -14,46 +14,45 @@
*/
package org.springframework.security.cas.authentication;
/**
* Implementation of @link {@link StatelessTicketCache} that has no backing cache. Useful
* Implementation of @link {@link StatelessTicketCache} that has no backing cache. Useful
* in instances where storing of tickets for stateless session management is not required.
* <p>
* This is the default StatelessTicketCache of the @link {@link CasAuthenticationProvider} to
* eliminate the unnecessary dependency on EhCache that applications have even if they are not using
* the stateless session management.
* This is the default StatelessTicketCache of the @link {@link CasAuthenticationProvider}
* to eliminate the unnecessary dependency on EhCache that applications have even if they
* are not using the stateless session management.
*
* @author Scott Battaglia
*
*@see CasAuthenticationProvider
* @see CasAuthenticationProvider
*/
public final class NullStatelessTicketCache implements StatelessTicketCache {
/**
* @return null since we are not storing any tickets.
*/
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
return null;
}
/**
* @return null since we are not storing any tickets.
*/
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
return null;
}
/**
* This is a no-op since we are not storing tickets.
*/
public void putTicketInCache(final CasAuthenticationToken token) {
// nothing to do
}
/**
* This is a no-op since we are not storing tickets.
*/
public void putTicketInCache(final CasAuthenticationToken token) {
// nothing to do
}
/**
* This is a no-op since we are not storing tickets.
*/
public void removeTicketFromCache(final CasAuthenticationToken token) {
// nothing to do
}
/**
* This is a no-op since we are not storing tickets.
*/
public void removeTicketFromCache(final CasAuthenticationToken token) {
// nothing to do
}
/**
* This is a no-op since we are not storing tickets.
*/
public void removeTicketFromCache(final String serviceTicket) {
// nothing to do
}
/**
* This is a no-op since we are not storing tickets.
*/
public void removeTicketFromCache(final String serviceTicket) {
// nothing to do
}
}

View File

@ -20,7 +20,6 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.cache.Cache;
import org.springframework.util.Assert;
/**
* Caches tickets using a Spring IoC defined {@link Cache}.
*
@ -29,52 +28,59 @@ import org.springframework.util.Assert;
*
*/
public class SpringCacheBasedTicketCache implements StatelessTicketCache {
//~ Static fields/initializers =====================================================================================
// ~ Static fields/initializers
// =====================================================================================
private static final Log logger = LogFactory.getLog(SpringCacheBasedTicketCache.class);
private static final Log logger = LogFactory
.getLog(SpringCacheBasedTicketCache.class);
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private final Cache cache;
private final Cache cache;
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
public SpringCacheBasedTicketCache(Cache cache) throws Exception {
Assert.notNull(cache, "cache mandatory");
this.cache = cache;
}
public SpringCacheBasedTicketCache(Cache cache) throws Exception {
Assert.notNull(cache, "cache mandatory");
this.cache = cache;
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
final Cache.ValueWrapper element = serviceTicket != null ? cache.get(serviceTicket) : null;
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
final Cache.ValueWrapper element = serviceTicket != null ? cache
.get(serviceTicket) : null;
if (logger.isDebugEnabled()) {
logger.debug("Cache hit: " + (element != null) + "; service ticket: " + serviceTicket);
}
if (logger.isDebugEnabled()) {
logger.debug("Cache hit: " + (element != null) + "; service ticket: "
+ serviceTicket);
}
return element == null ? null : (CasAuthenticationToken) element.get();
}
return element == null ? null : (CasAuthenticationToken) element.get();
}
public void putTicketInCache(final CasAuthenticationToken token) {
String key = token.getCredentials().toString();
public void putTicketInCache(final CasAuthenticationToken token) {
String key = token.getCredentials().toString();
if (logger.isDebugEnabled()) {
logger.debug("Cache put: " + key);
}
if (logger.isDebugEnabled()) {
logger.debug("Cache put: " + key);
}
cache.put(key, token);
}
cache.put(key, token);
}
public void removeTicketFromCache(final CasAuthenticationToken token) {
if (logger.isDebugEnabled()) {
logger.debug("Cache remove: " + token.getCredentials().toString());
}
public void removeTicketFromCache(final CasAuthenticationToken token) {
if (logger.isDebugEnabled()) {
logger.debug("Cache remove: " + token.getCredentials().toString());
}
this.removeTicketFromCache(token.getCredentials().toString());
}
this.removeTicketFromCache(token.getCredentials().toString());
}
public void removeTicketFromCache(final String serviceTicket) {
cache.evict(serviceTicket);
}
public void removeTicketFromCache(final String serviceTicket) {
cache.evict(serviceTicket);
}
}

View File

@ -19,97 +19,95 @@ package org.springframework.security.cas.authentication;
* Caches CAS service tickets and CAS proxy tickets for stateless connections.
*
* <p>
* When a service ticket or proxy ticket is validated against the CAS server,
* it is unable to be used again. Most types of callers are stateful and are
* associated with a given <code>HttpSession</code>. This allows the
* affirmative CAS validation outcome to be stored in the
* <code>HttpSession</code>, meaning the removal of the ticket from the CAS
* When a service ticket or proxy ticket is validated against the CAS server, it is unable
* to be used again. Most types of callers are stateful and are associated with a given
* <code>HttpSession</code>. This allows the affirmative CAS validation outcome to be
* stored in the <code>HttpSession</code>, meaning the removal of the ticket from the CAS
* server is not an issue.
* </p>
*
* <P>
* Stateless callers, such as remoting protocols, cannot take advantage of
* <code>HttpSession</code>. If the stateless caller is located a significant
* network distance from the CAS server, acquiring a fresh service ticket or
* proxy ticket for each invocation would be expensive.
* <code>HttpSession</code>. If the stateless caller is located a significant network
* distance from the CAS server, acquiring a fresh service ticket or proxy ticket for each
* invocation would be expensive.
* </p>
*
* <P>
* To avoid this issue with stateless callers, it is expected stateless callers
* will obtain a single service ticket or proxy ticket, and then present this
* same ticket to the Spring Security secured application on each
* occasion. As no <code>HttpSession</code> is available for such callers, the
* affirmative CAS validation outcome cannot be stored in this location.
* To avoid this issue with stateless callers, it is expected stateless callers will
* obtain a single service ticket or proxy ticket, and then present this same ticket to
* the Spring Security secured application on each occasion. As no
* <code>HttpSession</code> is available for such callers, the affirmative CAS validation
* outcome cannot be stored in this location.
* </p>
*
* <P>
* The <code>StatelessTicketCache</code> enables the service tickets and proxy
* tickets belonging to stateless callers to be placed in a cache. This
* in-memory cache stores the <code>CasAuthenticationToken</code>, effectively
* providing the same capability as a <code>HttpSession</code> with the ticket
* identifier being the key rather than a session identifier.
* The <code>StatelessTicketCache</code> enables the service tickets and proxy tickets
* belonging to stateless callers to be placed in a cache. This in-memory cache stores the
* <code>CasAuthenticationToken</code>, effectively providing the same capability as a
* <code>HttpSession</code> with the ticket identifier being the key rather than a session
* identifier.
* </p>
*
* <P>
* Implementations should provide a reasonable timeout on stored entries, such
* that the stateless caller are not required to unnecessarily acquire fresh
* CAS service tickets or proxy tickets.
* Implementations should provide a reasonable timeout on stored entries, such that the
* stateless caller are not required to unnecessarily acquire fresh CAS service tickets or
* proxy tickets.
* </p>
*
* @author Ben Alex
*/
public interface StatelessTicketCache {
//~ Methods ================================================================
// ~ Methods ================================================================
/**
* Retrieves the <code>CasAuthenticationToken</code> associated with the
* specified ticket.
*
* <P>
* If not found, returns a
* <code>null</code><code>CasAuthenticationToken</code>.
* </p>
*
* @return the fully populated authentication token
*/
CasAuthenticationToken getByTicketId(String serviceTicket);
/**
* Retrieves the <code>CasAuthenticationToken</code> associated with the specified
* ticket.
*
* <P>
* If not found, returns a <code>null</code><code>CasAuthenticationToken</code>.
* </p>
*
* @return the fully populated authentication token
*/
CasAuthenticationToken getByTicketId(String serviceTicket);
/**
* Adds the specified <code>CasAuthenticationToken</code> to the cache.
*
* <P>
* The {@link CasAuthenticationToken#getCredentials()} method is used to
* retrieve the service ticket number.
* </p>
*
* @param token to be added to the cache
*/
void putTicketInCache(CasAuthenticationToken token);
/**
* Adds the specified <code>CasAuthenticationToken</code> to the cache.
*
* <P>
* The {@link CasAuthenticationToken#getCredentials()} method is used to retrieve the
* service ticket number.
* </p>
*
* @param token to be added to the cache
*/
void putTicketInCache(CasAuthenticationToken token);
/**
* Removes the specified ticket from the cache, as per {@link
* #removeTicketFromCache(String)}.
*
* <P>
* Implementations should use {@link
* CasAuthenticationToken#getCredentials()} to obtain the ticket and then
* delegate to to the {@link #removeTicketFromCache(String)} method.
* </p>
*
* @param token to be removed
*/
void removeTicketFromCache(CasAuthenticationToken token);
/**
* Removes the specified ticket from the cache, as per
* {@link #removeTicketFromCache(String)}.
*
* <P>
* Implementations should use {@link CasAuthenticationToken#getCredentials()} to
* obtain the ticket and then delegate to to the
* {@link #removeTicketFromCache(String)} method.
* </p>
*
* @param token to be removed
*/
void removeTicketFromCache(CasAuthenticationToken token);
/**
* Removes the specified ticket from the cache, meaning that future calls
* will require a new service ticket.
*
* <P>
* This is in case applications wish to provide a session termination
* capability for their stateless clients.
* </p>
*
* @param serviceTicket to be removed
*/
void removeTicketFromCache(String serviceTicket);
/**
* Removes the specified ticket from the cache, meaning that future calls will require
* a new service ticket.
*
* <P>
* This is in case applications wish to provide a session termination capability for
* their stateless clients.
* </p>
*
* @param serviceTicket to be removed
*/
void removeTicketFromCache(String serviceTicket);
}

View File

@ -20,25 +20,28 @@ import org.springframework.security.core.userdetails.AuthenticationUserDetailsSe
import org.springframework.security.core.userdetails.UserDetails;
/**
* Abstract class for using the provided CAS assertion to construct a new User object. This generally is most
* useful when combined with a SAML-based response from the CAS Server/client.
* Abstract class for using the provided CAS assertion to construct a new User object.
* This generally is most useful when combined with a SAML-based response from the CAS
* Server/client.
*
* @author Scott Battaglia
* @since 3.0
*/
public abstract class AbstractCasAssertionUserDetailsService
implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {
public abstract class AbstractCasAssertionUserDetailsService implements
AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {
public final UserDetails loadUserDetails(final CasAssertionAuthenticationToken token) {
return loadUserDetails(token.getAssertion());
}
public final UserDetails loadUserDetails(final CasAssertionAuthenticationToken token) {
return loadUserDetails(token.getAssertion());
}
/**
* Protected template method for construct a {@link org.springframework.security.core.userdetails.UserDetails} via the supplied CAS
* assertion.
*
* @param assertion the assertion to use to construct the new UserDetails. CANNOT be NULL.
* @return the newly constructed UserDetails.
*/
protected abstract UserDetails loadUserDetails(Assertion assertion);
/**
* Protected template method for construct a
* {@link org.springframework.security.core.userdetails.UserDetails} via the supplied
* CAS assertion.
*
* @param assertion the assertion to use to construct the new UserDetails. CANNOT be
* NULL.
* @return the newly constructed UserDetails.
*/
protected abstract UserDetails loadUserDetails(Assertion assertion);
}

View File

@ -25,61 +25,71 @@ import java.util.List;
import java.util.ArrayList;
/**
* Populates the {@link org.springframework.security.core.GrantedAuthority}s for a user by reading a list of attributes that were returned as
* part of the CAS response. Each attribute is read and each value of the attribute is turned into a GrantedAuthority. If the attribute has no
* value then its not added.
* Populates the {@link org.springframework.security.core.GrantedAuthority}s for a user by
* reading a list of attributes that were returned as part of the CAS response. Each
* attribute is read and each value of the attribute is turned into a GrantedAuthority. If
* the attribute has no value then its not added.
*
* @author Scott Battaglia
* @since 3.0
*/
public final class GrantedAuthorityFromAssertionAttributesUserDetailsService extends AbstractCasAssertionUserDetailsService {
public final class GrantedAuthorityFromAssertionAttributesUserDetailsService extends
AbstractCasAssertionUserDetailsService {
private static final String NON_EXISTENT_PASSWORD_VALUE = "NO_PASSWORD";
private static final String NON_EXISTENT_PASSWORD_VALUE = "NO_PASSWORD";
private final String[] attributes;
private final String[] attributes;
private boolean convertToUpperCase = true;
private boolean convertToUpperCase = true;
public GrantedAuthorityFromAssertionAttributesUserDetailsService(final String[] attributes) {
Assert.notNull(attributes, "attributes cannot be null.");
Assert.isTrue(attributes.length > 0, "At least one attribute is required to retrieve roles from.");
this.attributes = attributes;
}
public GrantedAuthorityFromAssertionAttributesUserDetailsService(
final String[] attributes) {
Assert.notNull(attributes, "attributes cannot be null.");
Assert.isTrue(attributes.length > 0,
"At least one attribute is required to retrieve roles from.");
this.attributes = attributes;
}
@SuppressWarnings("unchecked")
@Override
protected UserDetails loadUserDetails(final Assertion assertion) {
final List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
@SuppressWarnings("unchecked")
@Override
protected UserDetails loadUserDetails(final Assertion assertion) {
final List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
for (final String attribute : this.attributes) {
final Object value = assertion.getPrincipal().getAttributes().get(attribute);
for (final String attribute : this.attributes) {
final Object value = assertion.getPrincipal().getAttributes().get(attribute);
if (value == null) {
continue;
}
if (value == null) {
continue;
}
if (value instanceof List) {
final List list = (List) value;
if (value instanceof List) {
final List list = (List) value;
for (final Object o : list) {
grantedAuthorities.add(new SimpleGrantedAuthority(this.convertToUpperCase ? o.toString().toUpperCase() : o.toString()));
}
for (final Object o : list) {
grantedAuthorities.add(new SimpleGrantedAuthority(
this.convertToUpperCase ? o.toString().toUpperCase() : o
.toString()));
}
} else {
grantedAuthorities.add(new SimpleGrantedAuthority(this.convertToUpperCase ? value.toString().toUpperCase() : value.toString()));
}
}
else {
grantedAuthorities.add(new SimpleGrantedAuthority(
this.convertToUpperCase ? value.toString().toUpperCase() : value
.toString()));
}
}
}
return new User(assertion.getPrincipal().getName(), NON_EXISTENT_PASSWORD_VALUE, true, true, true, true, grantedAuthorities);
}
return new User(assertion.getPrincipal().getName(), NON_EXISTENT_PASSWORD_VALUE,
true, true, true, true, grantedAuthorities);
}
/**
* Converts the returned attribute values to uppercase values.
*
* @param convertToUpperCase true if it should convert, false otherwise.
*/
public void setConvertToUpperCase(final boolean convertToUpperCase) {
this.convertToUpperCase = convertToUpperCase;
}
/**
* Converts the returned attribute values to uppercase values.
*
* @param convertToUpperCase true if it should convert, false otherwise.
*/
public void setConvertToUpperCase(final boolean convertToUpperCase) {
this.convertToUpperCase = convertToUpperCase;
}
}

View File

@ -28,122 +28,140 @@ import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* Used by the <code>ExceptionTranslationFilter</code> to commence authentication via the JA-SIG Central
* Authentication Service (CAS).
* Used by the <code>ExceptionTranslationFilter</code> to commence authentication via the
* JA-SIG Central Authentication Service (CAS).
* <p>
* The user's browser will be redirected to the JA-SIG CAS enterprise-wide login page.
* This page is specified by the <code>loginUrl</code> property. Once login is complete, the CAS login page will
* redirect to the page indicated by the <code>service</code> property. The <code>service</code> is a HTTP URL
* belonging to the current application. The <code>service</code> URL is monitored by the {@link CasAuthenticationFilter},
* which will validate the CAS login was successful.
* This page is specified by the <code>loginUrl</code> property. Once login is complete,
* the CAS login page will redirect to the page indicated by the <code>service</code>
* property. The <code>service</code> is a HTTP URL belonging to the current application.
* The <code>service</code> URL is monitored by the {@link CasAuthenticationFilter}, which
* will validate the CAS login was successful.
*
* @author Ben Alex
* @author Scott Battaglia
*/
public class CasAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean {
//~ Instance fields ================================================================================================
private ServiceProperties serviceProperties;
public class CasAuthenticationEntryPoint implements AuthenticationEntryPoint,
InitializingBean {
// ~ Instance fields
// ================================================================================================
private ServiceProperties serviceProperties;
private String loginUrl;
private String loginUrl;
/**
* Determines whether the Service URL should include the session id for the specific user. As of CAS 3.0.5, the
* session id will automatically be stripped. However, older versions of CAS (i.e. CAS 2), do not automatically
* strip the session identifier (this is a bug on the part of the older server implementations), so an option to
* disable the session encoding is provided for backwards compatibility.
*
* By default, encoding is enabled.
*/
private boolean encodeServiceUrlWithSessionId = true;
/**
* Determines whether the Service URL should include the session id for the specific
* user. As of CAS 3.0.5, the session id will automatically be stripped. However,
* older versions of CAS (i.e. CAS 2), do not automatically strip the session
* identifier (this is a bug on the part of the older server implementations), so an
* option to disable the session encoding is provided for backwards compatibility.
*
* By default, encoding is enabled.
*/
private boolean encodeServiceUrlWithSessionId = true;
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.hasLength(this.loginUrl, "loginUrl must be specified");
Assert.notNull(this.serviceProperties, "serviceProperties must be specified");
Assert.notNull(this.serviceProperties.getService(),"serviceProperties.getService() cannot be null.");
}
public void afterPropertiesSet() throws Exception {
Assert.hasLength(this.loginUrl, "loginUrl must be specified");
Assert.notNull(this.serviceProperties, "serviceProperties must be specified");
Assert.notNull(this.serviceProperties.getService(),
"serviceProperties.getService() cannot be null.");
}
public final void commence(final HttpServletRequest servletRequest, final HttpServletResponse response,
final AuthenticationException authenticationException) throws IOException, ServletException {
public final void commence(final HttpServletRequest servletRequest,
final HttpServletResponse response,
final AuthenticationException authenticationException) throws IOException,
ServletException {
final String urlEncodedService = createServiceUrl(servletRequest, response);
final String redirectUrl = createRedirectUrl(urlEncodedService);
final String urlEncodedService = createServiceUrl(servletRequest, response);
final String redirectUrl = createRedirectUrl(urlEncodedService);
preCommence(servletRequest, response);
preCommence(servletRequest, response);
response.sendRedirect(redirectUrl);
}
response.sendRedirect(redirectUrl);
}
/**
* Constructs a new Service Url. The default implementation relies on the CAS client to do the bulk of the work.
* @param request the HttpServletRequest
* @param response the HttpServlet Response
* @return the constructed service url. CANNOT be NULL.
*/
protected String createServiceUrl(final HttpServletRequest request, final HttpServletResponse response) {
return CommonUtils.constructServiceUrl(null, response, this.serviceProperties.getService(), null, this.serviceProperties.getArtifactParameter(), this.encodeServiceUrlWithSessionId);
}
/**
* Constructs a new Service Url. The default implementation relies on the CAS client
* to do the bulk of the work.
* @param request the HttpServletRequest
* @param response the HttpServlet Response
* @return the constructed service url. CANNOT be NULL.
*/
protected String createServiceUrl(final HttpServletRequest request,
final HttpServletResponse response) {
return CommonUtils.constructServiceUrl(null, response,
this.serviceProperties.getService(), null,
this.serviceProperties.getArtifactParameter(),
this.encodeServiceUrlWithSessionId);
}
/**
* Constructs the Url for Redirection to the CAS server. Default implementation relies on the CAS client to do the bulk of the work.
*
* @param serviceUrl the service url that should be included.
* @return the redirect url. CANNOT be NULL.
*/
protected String createRedirectUrl(final String serviceUrl) {
return CommonUtils.constructRedirectUrl(this.loginUrl, this.serviceProperties.getServiceParameter(), serviceUrl, this.serviceProperties.isSendRenew(), false);
}
/**
* Constructs the Url for Redirection to the CAS server. Default implementation relies
* on the CAS client to do the bulk of the work.
*
* @param serviceUrl the service url that should be included.
* @return the redirect url. CANNOT be NULL.
*/
protected String createRedirectUrl(final String serviceUrl) {
return CommonUtils.constructRedirectUrl(this.loginUrl,
this.serviceProperties.getServiceParameter(), serviceUrl,
this.serviceProperties.isSendRenew(), false);
}
/**
* Template method for you to do your own pre-processing before the redirect occurs.
*
* @param request the HttpServletRequest
* @param response the HttpServletResponse
*/
protected void preCommence(final HttpServletRequest request, final HttpServletResponse response) {
/**
* Template method for you to do your own pre-processing before the redirect occurs.
*
* @param request the HttpServletRequest
* @param response the HttpServletResponse
*/
protected void preCommence(final HttpServletRequest request,
final HttpServletResponse response) {
}
}
/**
* The enterprise-wide CAS login URL. Usually something like
* <code>https://www.mycompany.com/cas/login</code>.
*
* @return the enterprise-wide CAS login URL
*/
public final String getLoginUrl() {
return this.loginUrl;
}
/**
* The enterprise-wide CAS login URL. Usually something like
* <code>https://www.mycompany.com/cas/login</code>.
*
* @return the enterprise-wide CAS login URL
*/
public final String getLoginUrl() {
return this.loginUrl;
}
public final ServiceProperties getServiceProperties() {
return this.serviceProperties;
}
public final ServiceProperties getServiceProperties() {
return this.serviceProperties;
}
public final void setLoginUrl(final String loginUrl) {
this.loginUrl = loginUrl;
}
public final void setLoginUrl(final String loginUrl) {
this.loginUrl = loginUrl;
}
public final void setServiceProperties(final ServiceProperties serviceProperties) {
this.serviceProperties = serviceProperties;
}
public final void setServiceProperties(final ServiceProperties serviceProperties) {
this.serviceProperties = serviceProperties;
}
/**
* Sets whether to encode the service url with the session id or not.
*
* @param encodeServiceUrlWithSessionId whether to encode the service url with the session id or not.
*/
public final void setEncodeServiceUrlWithSessionId(final boolean encodeServiceUrlWithSessionId) {
this.encodeServiceUrlWithSessionId = encodeServiceUrlWithSessionId;
}
/**
* Sets whether to encode the service url with the session id or not.
*
* @param encodeServiceUrlWithSessionId whether to encode the service url with the
* session id or not.
*/
public final void setEncodeServiceUrlWithSessionId(
final boolean encodeServiceUrlWithSessionId) {
this.encodeServiceUrlWithSessionId = encodeServiceUrlWithSessionId;
}
/**
* Sets whether to encode the service url with the session id or not.
* @return whether to encode the service url with the session id or not.
*
*/
protected boolean getEncodeServiceUrlWithSessionId() {
return this.encodeServiceUrlWithSessionId;
}
/**
* Sets whether to encode the service url with the session id or not.
* @return whether to encode the service url with the session id or not.
*
*/
protected boolean getEncodeServiceUrlWithSessionId() {
return this.encodeServiceUrlWithSessionId;
}
}

View File

@ -43,51 +43,64 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
/**
* Processes a CAS service ticket, obtains proxy granting tickets, and processes proxy tickets.
* <h2>Service Tickets</h2>
* Processes a CAS service ticket, obtains proxy granting tickets, and processes proxy
* tickets. <h2>Service Tickets</h2>
* <p>
* A service ticket consists of an opaque ticket string. It arrives at this filter by the user's browser successfully
* authenticating using CAS, and then receiving a HTTP redirect to a <code>service</code>. The opaque ticket string is
* presented in the <code>ticket</code> request parameter.
* A service ticket consists of an opaque ticket string. It arrives at this filter by the
* user's browser successfully authenticating using CAS, and then receiving a HTTP
* redirect to a <code>service</code>. The opaque ticket string is presented in the
* <code>ticket</code> request parameter.
* <p>
* This filter monitors the <code>service</code> URL so it can
* receive the service ticket and process it. By default this filter processes the URL <tt>/login/cas</tt>.
* When processing this URL, the value of {@link ServiceProperties#getService()} is used as the <tt>service</tt> when validating
* the <code>ticket</code>. This means that it is important that {@link ServiceProperties#getService()} specifies the same value
* as the <tt>filterProcessesUrl</tt>.
* This filter monitors the <code>service</code> URL so it can receive the service ticket
* and process it. By default this filter processes the URL <tt>/login/cas</tt>. When
* processing this URL, the value of {@link ServiceProperties#getService()} is used as the
* <tt>service</tt> when validating the <code>ticket</code>. This means that it is
* important that {@link ServiceProperties#getService()} specifies the same value as the
* <tt>filterProcessesUrl</tt>.
* <p>
* Processing the service ticket involves creating a <code>UsernamePasswordAuthenticationToken</code> which
* uses {@link #CAS_STATEFUL_IDENTIFIER} for the <code>principal</code> and the opaque ticket string as the
* <code>credentials</code>.
* Processing the service ticket involves creating a
* <code>UsernamePasswordAuthenticationToken</code> which uses
* {@link #CAS_STATEFUL_IDENTIFIER} for the <code>principal</code> and the opaque ticket
* string as the <code>credentials</code>.
* <h2>Obtaining Proxy Granting Tickets</h2>
* <p>
* If specified, the filter can also monitor the <code>proxyReceptorUrl</code>. The filter will respond to requests matching
* this url so that the CAS Server can provide a PGT to the filter. Note that in addition to the <code>proxyReceptorUrl</code> a non-null
* <code>proxyGrantingTicketStorage</code> must be provided in order for the filter to respond to proxy receptor requests. By configuring
* a shared {@link ProxyGrantingTicketStorage} between the {@link TicketValidator} and the CasAuthenticationFilter one can have the
* CasAuthenticationFilter handle the proxying requirements for CAS.
* If specified, the filter can also monitor the <code>proxyReceptorUrl</code>. The filter
* will respond to requests matching this url so that the CAS Server can provide a PGT to
* the filter. Note that in addition to the <code>proxyReceptorUrl</code> a non-null
* <code>proxyGrantingTicketStorage</code> must be provided in order for the filter to
* respond to proxy receptor requests. By configuring a shared
* {@link ProxyGrantingTicketStorage} between the {@link TicketValidator} and the
* CasAuthenticationFilter one can have the CasAuthenticationFilter handle the proxying
* requirements for CAS.
* <h2>Proxy Tickets</h2>
* <p>
* The filter can process tickets present on any url. This is useful when wanting to process proxy tickets. In order for proxy
* tickets to get processed {@link ServiceProperties#isAuthenticateAllArtifacts()} must return <code>true</code>. Additionally,
* if the request is already authenticated, authentication will <b>not</b> occur. Last, {@link AuthenticationDetailsSource#buildDetails(Object)}
* must return a {@link ServiceAuthenticationDetails}. This can be accomplished using the {@link ServiceAuthenticationDetailsSource}.
* In this case {@link ServiceAuthenticationDetails#getServiceUrl()} will be used for the service url.
* The filter can process tickets present on any url. This is useful when wanting to
* process proxy tickets. In order for proxy tickets to get processed
* {@link ServiceProperties#isAuthenticateAllArtifacts()} must return <code>true</code>.
* Additionally, if the request is already authenticated, authentication will <b>not</b>
* occur. Last, {@link AuthenticationDetailsSource#buildDetails(Object)} must return a
* {@link ServiceAuthenticationDetails}. This can be accomplished using the
* {@link ServiceAuthenticationDetailsSource}. In this case
* {@link ServiceAuthenticationDetails#getServiceUrl()} will be used for the service url.
* <p>
* Processing the proxy ticket involves creating a <code>UsernamePasswordAuthenticationToken</code> which
* uses {@link #CAS_STATELESS_IDENTIFIER} for the <code>principal</code> and the opaque ticket string as the
* <code>credentials</code>. When a proxy ticket is successfully authenticated, the FilterChain continues and the
* Processing the proxy ticket involves creating a
* <code>UsernamePasswordAuthenticationToken</code> which uses
* {@link #CAS_STATELESS_IDENTIFIER} for the <code>principal</code> and the opaque ticket
* string as the <code>credentials</code>. When a proxy ticket is successfully
* authenticated, the FilterChain continues and the
* <code>authenticationSuccessHandler</code> is not used.
* <h2>Notes about the <code>AuthenticationManager</code></h2>
* <p>
* The configured <code>AuthenticationManager</code> is expected to provide a provider that can recognise
* <code>UsernamePasswordAuthenticationToken</code>s containing this special <code>principal</code> name, and process
* them accordingly by validation with the CAS server. Additionally, it should be capable of using the result of
* {@link ServiceAuthenticationDetails#getServiceUrl()} as the service when validating the ticket.
* The configured <code>AuthenticationManager</code> is expected to provide a provider
* that can recognise <code>UsernamePasswordAuthenticationToken</code>s containing this
* special <code>principal</code> name, and process them accordingly by validation with
* the CAS server. Additionally, it should be capable of using the result of
* {@link ServiceAuthenticationDetails#getServiceUrl()} as the service when validating the
* ticket.
* <h2>Example Configuration</h2>
* <p>
* An example configuration that supports service tickets, obtaining proxy granting tickets, and proxy tickets is
* illustrated below:
* An example configuration that supports service tickets, obtaining proxy granting
* tickets, and proxy tickets is illustrated below:
*
* <pre>
* &lt;b:bean id=&quot;serviceProperties&quot;
@ -157,236 +170,265 @@ import org.springframework.util.Assert;
* @author Rob Winch
*/
public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
//~ Static fields/initializers =====================================================================================
// ~ Static fields/initializers
// =====================================================================================
/** Used to identify a CAS request for a stateful user agent, such as a web browser. */
public static final String CAS_STATEFUL_IDENTIFIER = "_cas_stateful_";
/** Used to identify a CAS request for a stateful user agent, such as a web browser. */
public static final String CAS_STATEFUL_IDENTIFIER = "_cas_stateful_";
/**
* Used to identify a CAS request for a stateless user agent, such as a remoting protocol client (e.g.
* Hessian, Burlap, SOAP etc). Results in a more aggressive caching strategy being used, as the absence of a
* <code>HttpSession</code> will result in a new authentication attempt on every request.
*/
public static final String CAS_STATELESS_IDENTIFIER = "_cas_stateless_";
/**
* Used to identify a CAS request for a stateless user agent, such as a remoting
* protocol client (e.g. Hessian, Burlap, SOAP etc). Results in a more aggressive
* caching strategy being used, as the absence of a <code>HttpSession</code> will
* result in a new authentication attempt on every request.
*/
public static final String CAS_STATELESS_IDENTIFIER = "_cas_stateless_";
/**
* The last portion of the receptor url, i.e. /proxy/receptor
*/
private RequestMatcher proxyReceptorMatcher;
/**
* The last portion of the receptor url, i.e. /proxy/receptor
*/
private RequestMatcher proxyReceptorMatcher;
/**
* The backing storage to store ProxyGrantingTicket requests.
*/
private ProxyGrantingTicketStorage proxyGrantingTicketStorage;
/**
* The backing storage to store ProxyGrantingTicket requests.
*/
private ProxyGrantingTicketStorage proxyGrantingTicketStorage;
private String artifactParameter = ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER;
private String artifactParameter = ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER;
private boolean authenticateAllArtifacts;
private boolean authenticateAllArtifacts;
private AuthenticationFailureHandler proxyFailureHandler = new SimpleUrlAuthenticationFailureHandler();
private AuthenticationFailureHandler proxyFailureHandler = new SimpleUrlAuthenticationFailureHandler();
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
public CasAuthenticationFilter() {
super("/login/cas");
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
}
public CasAuthenticationFilter() {
super("/login/cas");
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
@Override
protected final void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
boolean continueFilterChain = proxyTicketRequest(serviceTicketRequest(request, response),request);
if(!continueFilterChain) {
super.successfulAuthentication(request, response, chain, authResult);
return;
}
@Override
protected final void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
boolean continueFilterChain = proxyTicketRequest(
serviceTicketRequest(request, response), request);
if (!continueFilterChain) {
super.successfulAuthentication(request, response, chain, authResult);
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
}
if (logger.isDebugEnabled()) {
logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
+ authResult);
}
SecurityContextHolder.getContext().setAuthentication(authResult);
SecurityContextHolder.getContext().setAuthentication(authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
authResult, this.getClass()));
}
chain.doFilter(request, response);
}
chain.doFilter(request, response);
}
@Override
public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response)
throws AuthenticationException, IOException {
// if the request is a proxy request process it and return null to indicate the request has been processed
if(proxyReceptorRequest(request)) {
logger.debug("Responding to proxy receptor request");
CommonUtils.readAndRespondToProxyReceptorRequest(request, response, this.proxyGrantingTicketStorage);
return null;
}
@Override
public Authentication attemptAuthentication(final HttpServletRequest request,
final HttpServletResponse response) throws AuthenticationException,
IOException {
// if the request is a proxy request process it and return null to indicate the
// request has been processed
if (proxyReceptorRequest(request)) {
logger.debug("Responding to proxy receptor request");
CommonUtils.readAndRespondToProxyReceptorRequest(request, response,
this.proxyGrantingTicketStorage);
return null;
}
final boolean serviceTicketRequest = serviceTicketRequest(request, response);
final String username = serviceTicketRequest ? CAS_STATEFUL_IDENTIFIER : CAS_STATELESS_IDENTIFIER;
String password = obtainArtifact(request);
final boolean serviceTicketRequest = serviceTicketRequest(request, response);
final String username = serviceTicketRequest ? CAS_STATEFUL_IDENTIFIER
: CAS_STATELESS_IDENTIFIER;
String password = obtainArtifact(request);
if (password == null) {
logger.debug("Failed to obtain an artifact (cas ticket)");
password = "";
}
if (password == null) {
logger.debug("Failed to obtain an artifact (cas ticket)");
password = "";
}
final UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
final UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
return this.getAuthenticationManager().authenticate(authRequest);
}
return this.getAuthenticationManager().authenticate(authRequest);
}
/**
* If present, gets the artifact (CAS ticket) from the {@link HttpServletRequest}.
* @param request
* @return if present the artifact from the {@link HttpServletRequest}, else null
*/
protected String obtainArtifact(HttpServletRequest request) {
return request.getParameter(artifactParameter);
}
/**
* If present, gets the artifact (CAS ticket) from the {@link HttpServletRequest}.
* @param request
* @return if present the artifact from the {@link HttpServletRequest}, else null
*/
protected String obtainArtifact(HttpServletRequest request) {
return request.getParameter(artifactParameter);
}
/**
* Overridden to provide proxying capabilities.
*/
protected boolean requiresAuthentication(final HttpServletRequest request, final HttpServletResponse response) {
final boolean serviceTicketRequest = serviceTicketRequest(request, response);
final boolean result = serviceTicketRequest || proxyReceptorRequest(request) || (proxyTicketRequest(serviceTicketRequest, request));
if(logger.isDebugEnabled()) {
logger.debug("requiresAuthentication = "+result);
}
return result;
}
/**
* Overridden to provide proxying capabilities.
*/
protected boolean requiresAuthentication(final HttpServletRequest request,
final HttpServletResponse response) {
final boolean serviceTicketRequest = serviceTicketRequest(request, response);
final boolean result = serviceTicketRequest || proxyReceptorRequest(request)
|| (proxyTicketRequest(serviceTicketRequest, request));
if (logger.isDebugEnabled()) {
logger.debug("requiresAuthentication = " + result);
}
return result;
}
/**
* Sets the {@link AuthenticationFailureHandler} for proxy requests.
* @param proxyFailureHandler
*/
public final void setProxyAuthenticationFailureHandler(
AuthenticationFailureHandler proxyFailureHandler) {
Assert.notNull(proxyFailureHandler,"proxyFailureHandler cannot be null");
this.proxyFailureHandler = proxyFailureHandler;
}
/**
* Sets the {@link AuthenticationFailureHandler} for proxy requests.
* @param proxyFailureHandler
*/
public final void setProxyAuthenticationFailureHandler(
AuthenticationFailureHandler proxyFailureHandler) {
Assert.notNull(proxyFailureHandler, "proxyFailureHandler cannot be null");
this.proxyFailureHandler = proxyFailureHandler;
}
/**
* Wraps the {@link AuthenticationFailureHandler} to distinguish between
* handling proxy ticket authentication failures and service ticket
* failures.
*/
@Override
public final void setAuthenticationFailureHandler(
AuthenticationFailureHandler failureHandler) {
super.setAuthenticationFailureHandler(new CasAuthenticationFailureHandler(failureHandler));
}
/**
* Wraps the {@link AuthenticationFailureHandler} to distinguish between handling
* proxy ticket authentication failures and service ticket failures.
*/
@Override
public final void setAuthenticationFailureHandler(
AuthenticationFailureHandler failureHandler) {
super.setAuthenticationFailureHandler(new CasAuthenticationFailureHandler(
failureHandler));
}
public final void setProxyReceptorUrl(final String proxyReceptorUrl) {
this.proxyReceptorMatcher = new AntPathRequestMatcher("/**" + proxyReceptorUrl);
}
public final void setProxyReceptorUrl(final String proxyReceptorUrl) {
this.proxyReceptorMatcher = new AntPathRequestMatcher("/**" + proxyReceptorUrl);
}
public final void setProxyGrantingTicketStorage(
final ProxyGrantingTicketStorage proxyGrantingTicketStorage) {
this.proxyGrantingTicketStorage = proxyGrantingTicketStorage;
}
public final void setProxyGrantingTicketStorage(
final ProxyGrantingTicketStorage proxyGrantingTicketStorage) {
this.proxyGrantingTicketStorage = proxyGrantingTicketStorage;
}
public final void setServiceProperties(final ServiceProperties serviceProperties) {
this.artifactParameter = serviceProperties.getArtifactParameter();
this.authenticateAllArtifacts = serviceProperties.isAuthenticateAllArtifacts();
}
public final void setServiceProperties(final ServiceProperties serviceProperties) {
this.artifactParameter = serviceProperties.getArtifactParameter();
this.authenticateAllArtifacts = serviceProperties.isAuthenticateAllArtifacts();
}
/**
* Indicates if the request is elgible to process a service ticket. This method exists for readability.
* @param request
* @param response
* @return
*/
private boolean serviceTicketRequest(final HttpServletRequest request, final HttpServletResponse response) {
boolean result = super.requiresAuthentication(request, response);
if(logger.isDebugEnabled()) {
logger.debug("serviceTicketRequest = "+result);
}
return result;
}
/**
* Indicates if the request is elgible to process a service ticket. This method exists
* for readability.
* @param request
* @param response
* @return
*/
private boolean serviceTicketRequest(final HttpServletRequest request,
final HttpServletResponse response) {
boolean result = super.requiresAuthentication(request, response);
if (logger.isDebugEnabled()) {
logger.debug("serviceTicketRequest = " + result);
}
return result;
}
/**
* Indicates if the request is elgible to process a proxy ticket.
* @param request
* @return
*/
private boolean proxyTicketRequest(final boolean serviceTicketRequest, final HttpServletRequest request) {
if(serviceTicketRequest) {
return false;
}
final boolean result = authenticateAllArtifacts && obtainArtifact(request) != null && !authenticated();
if(logger.isDebugEnabled()) {
logger.debug("proxyTicketRequest = "+result);
}
return result;
}
/**
* Indicates if the request is elgible to process a proxy ticket.
* @param request
* @return
*/
private boolean proxyTicketRequest(final boolean serviceTicketRequest,
final HttpServletRequest request) {
if (serviceTicketRequest) {
return false;
}
final boolean result = authenticateAllArtifacts
&& obtainArtifact(request) != null && !authenticated();
if (logger.isDebugEnabled()) {
logger.debug("proxyTicketRequest = " + result);
}
return result;
}
/**
* Determines if a user is already authenticated.
* @return
*/
private boolean authenticated() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication != null && authentication.isAuthenticated() && !(authentication instanceof AnonymousAuthenticationToken);
}
/**
* Indicates if the request is elgible to be processed as the proxy receptor.
* @param request
* @return
*/
private boolean proxyReceptorRequest(final HttpServletRequest request) {
final boolean result = proxyReceptorConfigured() && proxyReceptorMatcher.matches(request);
if(logger.isDebugEnabled()) {
logger.debug("proxyReceptorRequest = "+result);
}
return result;
}
/**
* Determines if a user is already authenticated.
* @return
*/
private boolean authenticated() {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
return authentication != null && authentication.isAuthenticated()
&& !(authentication instanceof AnonymousAuthenticationToken);
}
/**
* Determines if the {@link CasAuthenticationFilter} is configured to handle the proxy receptor requests.
*
* @return
*/
private boolean proxyReceptorConfigured() {
final boolean result = this.proxyGrantingTicketStorage != null && proxyReceptorMatcher != null;
if(logger.isDebugEnabled()) {
logger.debug("proxyReceptorConfigured = "+result);
}
return result;
}
/**
* Indicates if the request is elgible to be processed as the proxy receptor.
* @param request
* @return
*/
private boolean proxyReceptorRequest(final HttpServletRequest request) {
final boolean result = proxyReceptorConfigured()
&& proxyReceptorMatcher.matches(request);
if (logger.isDebugEnabled()) {
logger.debug("proxyReceptorRequest = " + result);
}
return result;
}
/**
* A wrapper for the AuthenticationFailureHandler that will flex the {@link AuthenticationFailureHandler} that is used. The value
* {@link CasAuthenticationFilter#setProxyAuthenticationFailureHandler(AuthenticationFailureHandler) will be used for proxy requests
* that fail. The value {@link CasAuthenticationFilter#setAuthenticationFailureHandler(AuthenticationFailureHandler)} will be used for
* service tickets that fail.
*
* @author Rob Winch
*/
private class CasAuthenticationFailureHandler implements AuthenticationFailureHandler {
private final AuthenticationFailureHandler serviceTicketFailureHandler;
public CasAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {
Assert.notNull(failureHandler,"failureHandler");
this.serviceTicketFailureHandler = failureHandler;
}
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception) throws IOException,
ServletException {
if(serviceTicketRequest(request, response)) {
serviceTicketFailureHandler.onAuthenticationFailure(request, response, exception);
}else {
proxyFailureHandler.onAuthenticationFailure(request, response, exception);
}
}
}
/**
* Determines if the {@link CasAuthenticationFilter} is configured to handle the proxy
* receptor requests.
*
* @return
*/
private boolean proxyReceptorConfigured() {
final boolean result = this.proxyGrantingTicketStorage != null
&& proxyReceptorMatcher != null;
if (logger.isDebugEnabled()) {
logger.debug("proxyReceptorConfigured = " + result);
}
return result;
}
/**
* A wrapper for the AuthenticationFailureHandler that will flex the
* {@link AuthenticationFailureHandler} that is used. The value
* {@link CasAuthenticationFilter#setProxyAuthenticationFailureHandler(AuthenticationFailureHandler)
* will be used for proxy requests that fail. The value
* {@link CasAuthenticationFilter#setAuthenticationFailureHandler(AuthenticationFailureHandler)}
* will be used for service tickets that fail.
*
* @author Rob Winch
*/
private class CasAuthenticationFailureHandler implements AuthenticationFailureHandler {
private final AuthenticationFailureHandler serviceTicketFailureHandler;
public CasAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {
Assert.notNull(failureHandler, "failureHandler");
this.serviceTicketFailureHandler = failureHandler;
}
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {
if (serviceTicketRequest(request, response)) {
serviceTicketFailureHandler.onAuthenticationFailure(request, response,
exception);
}
else {
proxyFailureHandler.onAuthenticationFailure(request, response, exception);
}
}
}
}

View File

@ -26,123 +26,128 @@ import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;
/**
* A default implementation of {@link ServiceAuthenticationDetails} that figures
* out the value for {@link #getServiceUrl()} by inspecting the current
* {@link HttpServletRequest} and using the current URL minus the artifact and
* the corresponding value.
* A default implementation of {@link ServiceAuthenticationDetails} that figures out the
* value for {@link #getServiceUrl()} by inspecting the current {@link HttpServletRequest}
* and using the current URL minus the artifact and the corresponding value.
*
* @author Rob Winch
*/
final class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails implements ServiceAuthenticationDetails {
private static final long serialVersionUID = 6192409090610517700L;
final class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails
implements ServiceAuthenticationDetails {
private static final long serialVersionUID = 6192409090610517700L;
//~ Instance fields ================================================================================================
// ~ Instance fields
// ================================================================================================
private final String serviceUrl;
private final String serviceUrl;
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
/**
* Creates a new instance
* @param request
* the current {@link HttpServletRequest} to obtain the
* {@link #getServiceUrl()} from.
* @param artifactPattern
* the {@link Pattern} that will be used to clean up the query
* string from containing the artifact name and value. This can
* be created using {@link #createArtifactPattern(String)}.
*/
DefaultServiceAuthenticationDetails(String casService, HttpServletRequest request, Pattern artifactPattern) throws MalformedURLException {
super(request);
URL casServiceUrl = new URL(casService);
int port = getServicePort(casServiceUrl);
final String query = getQueryString(request,artifactPattern);
this.serviceUrl = UrlUtils.buildFullRequestUrl(casServiceUrl.getProtocol(),
casServiceUrl.getHost(), port,
request.getRequestURI(), query);
}
/**
* Creates a new instance
* @param request the current {@link HttpServletRequest} to obtain the
* {@link #getServiceUrl()} from.
* @param artifactPattern the {@link Pattern} that will be used to clean up the query
* string from containing the artifact name and value. This can be created using
* {@link #createArtifactPattern(String)}.
*/
DefaultServiceAuthenticationDetails(String casService, HttpServletRequest request,
Pattern artifactPattern) throws MalformedURLException {
super(request);
URL casServiceUrl = new URL(casService);
int port = getServicePort(casServiceUrl);
final String query = getQueryString(request, artifactPattern);
this.serviceUrl = UrlUtils.buildFullRequestUrl(casServiceUrl.getProtocol(),
casServiceUrl.getHost(), port, request.getRequestURI(), query);
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
/**
* Returns the current URL minus the artifact parameter and its value, if present.
* @see org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails#getServiceUrl()
*/
public String getServiceUrl() {
return serviceUrl;
}
/**
* Returns the current URL minus the artifact parameter and its value, if present.
* @see org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails#getServiceUrl()
*/
public String getServiceUrl() {
return serviceUrl;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result
+ serviceUrl.hashCode();
return result;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + serviceUrl.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj) || !(obj instanceof DefaultServiceAuthenticationDetails)) {
return false;
}
ServiceAuthenticationDetails that = (ServiceAuthenticationDetails) obj;
return serviceUrl.equals(that.getServiceUrl());
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj) || !(obj instanceof DefaultServiceAuthenticationDetails)) {
return false;
}
ServiceAuthenticationDetails that = (ServiceAuthenticationDetails) obj;
return serviceUrl.equals(that.getServiceUrl());
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append(super.toString());
result.append("ServiceUrl: ");
result.append(serviceUrl);
return result.toString();
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append(super.toString());
result.append("ServiceUrl: ");
result.append(serviceUrl);
return result.toString();
}
/**
* If present, removes the artifactParameterName and the corresponding value from the query String.
* @param request
* @return the query String minus the artifactParameterName and the corresponding value.
*/
private String getQueryString(final HttpServletRequest request, final Pattern artifactPattern) {
final String query = request.getQueryString();
if(query == null) {
return null;
}
final String result = artifactPattern.matcher(query).replaceFirst("");
if(result.length() == 0) {
return null;
}
// strip off the trailing & only if the artifact was the first query param
return result.startsWith("&") ? result.substring(1) : result;
}
/**
* If present, removes the artifactParameterName and the corresponding value from the
* query String.
* @param request
* @return the query String minus the artifactParameterName and the corresponding
* value.
*/
private String getQueryString(final HttpServletRequest request,
final Pattern artifactPattern) {
final String query = request.getQueryString();
if (query == null) {
return null;
}
final String result = artifactPattern.matcher(query).replaceFirst("");
if (result.length() == 0) {
return null;
}
// strip off the trailing & only if the artifact was the first query param
return result.startsWith("&") ? result.substring(1) : result;
}
/**
* Creates a {@link Pattern} that can be passed into the constructor. This
* allows the {@link Pattern} to be reused for every instance of
* {@link DefaultServiceAuthenticationDetails}.
*
* @param artifactParameterName
* @return
*/
static Pattern createArtifactPattern(String artifactParameterName) {
Assert.hasLength(artifactParameterName);
return Pattern.compile("&?"+Pattern.quote(artifactParameterName)+"=[^&]*");
}
/**
* Creates a {@link Pattern} that can be passed into the constructor. This allows the
* {@link Pattern} to be reused for every instance of
* {@link DefaultServiceAuthenticationDetails}.
*
* @param artifactParameterName
* @return
*/
static Pattern createArtifactPattern(String artifactParameterName) {
Assert.hasLength(artifactParameterName);
return Pattern.compile("&?" + Pattern.quote(artifactParameterName) + "=[^&]*");
}
/**
* Gets the port from the casServiceURL ensuring to return the proper value if the default port is being used.
* @param casServiceUrl the casServerUrl to be used (i.e. "https://example.com/context/login/cas")
* @return the port that is configured for the casServerUrl
*/
private static int getServicePort(URL casServiceUrl) {
int port = casServiceUrl.getPort();
if(port == -1) {
port = casServiceUrl.getDefaultPort();
}
return port;
}
/**
* Gets the port from the casServiceURL ensuring to return the proper value if the
* default port is being used.
* @param casServiceUrl the casServerUrl to be used (i.e.
* "https://example.com/context/login/cas")
* @return the port that is configured for the casServerUrl
*/
private static int getServicePort(URL casServiceUrl) {
int port = casServiceUrl.getPort();
if (port == -1) {
port = casServiceUrl.getDefaultPort();
}
return port;
}
}

View File

@ -22,10 +22,9 @@ import org.springframework.security.cas.authentication.CasAuthenticationProvider
import org.springframework.security.core.Authentication;
/**
* In order for the {@link CasAuthenticationProvider} to provide the correct
* service url to authenticate the ticket, the returned value of
* {@link Authentication#getDetails()} should implement this interface when
* tickets can be sent to any URL rather than only
* In order for the {@link CasAuthenticationProvider} to provide the correct service url
* to authenticate the ticket, the returned value of {@link Authentication#getDetails()}
* should implement this interface when tickets can be sent to any URL rather than only
* {@link ServiceProperties#getService()}.
*
* @author Rob Winch
@ -34,10 +33,10 @@ import org.springframework.security.core.Authentication;
*/
public interface ServiceAuthenticationDetails extends Serializable {
/**
* Gets the absolute service url (i.e. https://example.com/service/).
*
* @return the service url. Cannot be <code>null</code>.
*/
String getServiceUrl();
/**
* Gets the absolute service url (i.e. https://example.com/service/).
*
* @return the service url. Cannot be <code>null</code>.
*/
String getServiceUrl();
}

View File

@ -27,58 +27,66 @@ import org.springframework.util.Assert;
/**
* The {@code AuthenticationDetailsSource} that is set on the
* {@code CasAuthenticationFilter} should return a value that implements
* {@code ServiceAuthenticationDetails} if the application needs to authenticate
* dynamic service urls. The
* {@code ServiceAuthenticationDetailsSource#buildDetails(HttpServletRequest)}
* creates a default {@code ServiceAuthenticationDetails}.
* {@code ServiceAuthenticationDetails} if the application needs to authenticate dynamic
* service urls. The
* {@code ServiceAuthenticationDetailsSource#buildDetails(HttpServletRequest)} creates a
* default {@code ServiceAuthenticationDetails}.
*
* @author Rob Winch
*/
public class ServiceAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest,
ServiceAuthenticationDetails> {
//~ Instance fields ================================================================================================
public class ServiceAuthenticationDetailsSource implements
AuthenticationDetailsSource<HttpServletRequest, ServiceAuthenticationDetails> {
// ~ Instance fields
// ================================================================================================
private final Pattern artifactPattern;
private final Pattern artifactPattern;
private ServiceProperties serviceProperties;
private ServiceProperties serviceProperties;
//~ Constructors ===================================================================================================
// ~ Constructors
// ===================================================================================================
/**
* Creates an implementation that uses the specified ServiceProperites and the default CAS artifactParameterName.
*
* @param serviceProperties The ServiceProperties to use to construct the serviceUrl.
*/
public ServiceAuthenticationDetailsSource(ServiceProperties serviceProperties) {
this(serviceProperties,ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER);
}
/**
* Creates an implementation that uses the specified ServiceProperites and the default
* CAS artifactParameterName.
*
* @param serviceProperties The ServiceProperties to use to construct the serviceUrl.
*/
public ServiceAuthenticationDetailsSource(ServiceProperties serviceProperties) {
this(serviceProperties, ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER);
}
/**
* Creates an implementation that uses the specified artifactParameterName
*
* @param serviceProperties The ServiceProperties to use to construct the serviceUrl.
* @param artifactParameterName
* the artifactParameterName that is removed from the current
* URL. The result becomes the service url. Cannot be null and
* cannot be an empty String.
*/
public ServiceAuthenticationDetailsSource(ServiceProperties serviceProperties, String artifactParameterName) {
Assert.notNull(serviceProperties, "serviceProperties cannot be null");
this.serviceProperties = serviceProperties;
this.artifactPattern = DefaultServiceAuthenticationDetails.createArtifactPattern(artifactParameterName);
}
/**
* Creates an implementation that uses the specified artifactParameterName
*
* @param serviceProperties The ServiceProperties to use to construct the serviceUrl.
* @param artifactParameterName the artifactParameterName that is removed from the
* current URL. The result becomes the service url. Cannot be null and cannot be an
* empty String.
*/
public ServiceAuthenticationDetailsSource(ServiceProperties serviceProperties,
String artifactParameterName) {
Assert.notNull(serviceProperties, "serviceProperties cannot be null");
this.serviceProperties = serviceProperties;
this.artifactPattern = DefaultServiceAuthenticationDetails
.createArtifactPattern(artifactParameterName);
}
//~ Methods ========================================================================================================
// ~ Methods
// ========================================================================================================
/**
* @param context the {@code HttpServletRequest} object.
* @return the {@code ServiceAuthenticationDetails} containing information about the current request
*/
public ServiceAuthenticationDetails buildDetails(HttpServletRequest context) {
try {
return new DefaultServiceAuthenticationDetails(serviceProperties.getService(),context,artifactPattern);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
/**
* @param context the {@code HttpServletRequest} object.
* @return the {@code ServiceAuthenticationDetails} containing information about the
* current request
*/
public ServiceAuthenticationDetails buildDetails(HttpServletRequest context) {
try {
return new DefaultServiceAuthenticationDetails(
serviceProperties.getService(), context, artifactPattern);
}
catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -3,3 +3,4 @@
* credentials using CAS.
*/
package org.springframework.security.cas.web.authentication;

View File

@ -17,15 +17,17 @@ import org.springframework.security.core.userdetails.User;
*/
public abstract class AbstractStatelessTicketCacheTests {
protected CasAuthenticationToken getToken() {
List<String> proxyList = new ArrayList<String>();
proxyList.add("https://localhost/newPortal/login/cas");
protected CasAuthenticationToken getToken() {
List<String> proxyList = new ArrayList<String>();
proxyList.add("https://localhost/newPortal/login/cas");
User user = new User("rod", "password", true, true, true, true, AuthorityUtils.createAuthorityList("ROLE_ONE", "ROLE_TWO"));
final Assertion assertion = new AssertionImpl("rod");
User user = new User("rod", "password", true, true, true, true,
AuthorityUtils.createAuthorityList("ROLE_ONE", "ROLE_TWO"));
final Assertion assertion = new AssertionImpl("rod");
return new CasAuthenticationToken("key", user, "ST-0-ER94xMJmn6pha35CQRoZ",
AuthorityUtils.createAuthorityList("ROLE_ONE", "ROLE_TWO"), user, assertion);
}
return new CasAuthenticationToken("key", user, "ST-0-ER94xMJmn6pha35CQRoZ",
AuthorityUtils.createAuthorityList("ROLE_ONE", "ROLE_TWO"), user,
assertion);
}
}

View File

@ -41,7 +41,6 @@ import org.springframework.security.web.authentication.WebAuthenticationDetails;
import java.util.*;
/**
* Tests {@link CasAuthenticationProvider}.
*
@ -50,365 +49,380 @@ import java.util.*;
*/
@SuppressWarnings("unchecked")
public class CasAuthenticationProviderTests {
//~ Methods ========================================================================================================
private UserDetails makeUserDetails() {
return new User("user", "password", true, true, true, true,
AuthorityUtils.createAuthorityList("ROLE_ONE", "ROLE_TWO"));
}
private UserDetails makeUserDetailsFromAuthoritiesPopulator() {
return new User("user", "password", true, true, true, true,
AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B"));
}
private ServiceProperties makeServiceProperties() {
final ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setSendRenew(false);
serviceProperties.setService("http://test.com");
return serviceProperties;
}
@Test
public void statefulAuthenticationIsSuccessful() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
StatelessTicketCache cache = new MockStatelessTicketCache();
cap.setStatelessTicketCache(cache);
cap.setServiceProperties(makeServiceProperties());
cap.setTicketValidator(new MockTicketValidator(true));
cap.afterPropertiesSet();
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER, "ST-123");
token.setDetails("details");
Authentication result = cap.authenticate(token);
// Confirm ST-123 was NOT added to the cache
assertTrue(cache.getByTicketId("ST-456") == null);
if (!(result instanceof CasAuthenticationToken)) {
fail("Should have returned a CasAuthenticationToken");
}
CasAuthenticationToken casResult = (CasAuthenticationToken) result;
assertEquals(makeUserDetailsFromAuthoritiesPopulator(), casResult.getPrincipal());
assertEquals("ST-123", casResult.getCredentials());
assertTrue(casResult.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_A")));
assertTrue(casResult.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_B")));
assertEquals(cap.getKey().hashCode(), casResult.getKeyHash());
assertEquals("details", casResult.getDetails());
// Now confirm the CasAuthenticationToken is automatically re-accepted.
// To ensure TicketValidator not called again, set it to deliver an exception...
cap.setTicketValidator(new MockTicketValidator(false));
Authentication laterResult = cap.authenticate(result);
assertEquals(result, laterResult);
}
@Test
public void statelessAuthenticationIsSuccessful() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
StatelessTicketCache cache = new MockStatelessTicketCache();
cap.setStatelessTicketCache(cache);
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, "ST-456");
token.setDetails("details");
Authentication result = cap.authenticate(token);
// Confirm ST-456 was added to the cache
assertTrue(cache.getByTicketId("ST-456") != null);
if (!(result instanceof CasAuthenticationToken)) {
fail("Should have returned a CasAuthenticationToken");
}
assertEquals(makeUserDetailsFromAuthoritiesPopulator(), result.getPrincipal());
assertEquals("ST-456", result.getCredentials());
assertEquals("details", result.getDetails());
// Now try to authenticate again. To ensure TicketValidator not
// called again, set it to deliver an exception...
cap.setTicketValidator(new MockTicketValidator(false));
// Previously created UsernamePasswordAuthenticationToken is OK
Authentication newResult = cap.authenticate(token);
assertEquals(makeUserDetailsFromAuthoritiesPopulator(), newResult.getPrincipal());
assertEquals("ST-456", newResult.getCredentials());
}
@Test
public void authenticateAllNullService() throws Exception {
String serviceUrl = "https://service/context";
ServiceAuthenticationDetails details = mock(ServiceAuthenticationDetails.class);
when(details.getServiceUrl()).thenReturn(serviceUrl);
TicketValidator validator = mock(TicketValidator.class);
when(validator.validate(any(String.class),any(String.class))).thenReturn(new AssertionImpl("rod"));
ServiceProperties serviceProperties = makeServiceProperties();
serviceProperties.setAuthenticateAllArtifacts(true);
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setTicketValidator(validator);
cap.setServiceProperties(serviceProperties);
cap.afterPropertiesSet();
String ticket = "ST-456";
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, ticket);
Authentication result = cap.authenticate(token);
}
@Test
public void authenticateAllAuthenticationIsSuccessful() throws Exception {
String serviceUrl = "https://service/context";
ServiceAuthenticationDetails details = mock(ServiceAuthenticationDetails.class);
when(details.getServiceUrl()).thenReturn(serviceUrl);
TicketValidator validator = mock(TicketValidator.class);
when(validator.validate(any(String.class),any(String.class))).thenReturn(new AssertionImpl("rod"));
ServiceProperties serviceProperties = makeServiceProperties();
serviceProperties.setAuthenticateAllArtifacts(true);
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setTicketValidator(validator);
cap.setServiceProperties(serviceProperties);
cap.afterPropertiesSet();
String ticket = "ST-456";
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, ticket);
Authentication result = cap.authenticate(token);
verify(validator).validate(ticket, serviceProperties.getService());
serviceProperties.setAuthenticateAllArtifacts(true);
result = cap.authenticate(token);
verify(validator,times(2)).validate(ticket, serviceProperties.getService());
token.setDetails(details);
result = cap.authenticate(token);
verify(validator).validate(ticket, serviceUrl);
serviceProperties.setAuthenticateAllArtifacts(false);
serviceProperties.setService(null);
cap.setServiceProperties(serviceProperties);
cap.afterPropertiesSet();
result = cap.authenticate(token);
verify(validator,times(2)).validate(ticket, serviceUrl);
token.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest()));
try {
cap.authenticate(token);
fail("Expected Exception");
}catch(IllegalStateException success) {}
cap.setServiceProperties(null);
cap.afterPropertiesSet();
try {
cap.authenticate(token);
fail("Expected Exception");
}catch(IllegalStateException success) {}
}
@Test(expected = BadCredentialsException.class)
public void missingTicketIdIsDetected() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
StatelessTicketCache cache = new MockStatelessTicketCache();
cap.setStatelessTicketCache(cache);
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER, "");
cap.authenticate(token);
}
@Test(expected = BadCredentialsException.class)
public void invalidKeyIsDetected() throws Exception {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
StatelessTicketCache cache = new MockStatelessTicketCache();
cap.setStatelessTicketCache(cache);
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
CasAuthenticationToken token = new CasAuthenticationToken("WRONG_KEY", makeUserDetails(), "credentials",
AuthorityUtils.createAuthorityList("XX"), makeUserDetails(), assertion);
cap.authenticate(token);
}
@Test(expected = IllegalArgumentException.class)
public void detectsMissingAuthoritiesPopulator() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
}
@Test(expected = IllegalArgumentException.class)
public void detectsMissingKey() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setStatelessTicketCache(new MockStatelessTicketCache());
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
}
@Test(expected = IllegalArgumentException.class)
public void detectsMissingStatelessTicketCache() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
// set this explicitly to null to test failure
cap.setStatelessTicketCache(null);
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
}
@Test(expected = IllegalArgumentException.class)
public void detectsMissingTicketValidator() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
}
@Test
public void gettersAndSettersMatch() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
// TODO disabled because why do we need to expose this?
// assertTrue(cap.getUserDetailsService() != null);
assertEquals("qwerty", cap.getKey());
assertTrue(cap.getStatelessTicketCache() != null);
assertTrue(cap.getTicketValidator() != null);
}
@Test
public void ignoresClassesItDoesNotSupport() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
TestingAuthenticationToken token = new TestingAuthenticationToken("user", "password", "ROLE_A");
assertFalse(cap.supports(TestingAuthenticationToken.class));
// Try it anyway
assertEquals(null, cap.authenticate(token));
}
@Test
public void ignoresUsernamePasswordAuthenticationTokensWithoutCasIdentifiersAsPrincipal() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("some_normal_user",
"password", AuthorityUtils.createAuthorityList("ROLE_A"));
assertEquals(null, cap.authenticate(token));
}
@Test
public void supportsRequiredTokens() {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
assertTrue(cap.supports(UsernamePasswordAuthenticationToken.class));
assertTrue(cap.supports(CasAuthenticationToken.class));
}
//~ Inner Classes ==================================================================================================
private class MockAuthoritiesPopulator implements AuthenticationUserDetailsService {
public UserDetails loadUserDetails(final Authentication token) throws UsernameNotFoundException {
return makeUserDetailsFromAuthoritiesPopulator();
}
}
private class MockStatelessTicketCache implements StatelessTicketCache {
private Map<String, CasAuthenticationToken> cache = new HashMap<String, CasAuthenticationToken>();
public CasAuthenticationToken getByTicketId(String serviceTicket) {
return cache.get(serviceTicket);
}
public void putTicketInCache(CasAuthenticationToken token) {
cache.put(token.getCredentials().toString(), token);
}
public void removeTicketFromCache(CasAuthenticationToken token) {
throw new UnsupportedOperationException("mock method not implemented");
}
public void removeTicketFromCache(String serviceTicket) {
throw new UnsupportedOperationException("mock method not implemented");
}
}
private class MockTicketValidator implements TicketValidator {
private boolean returnTicket;
public MockTicketValidator(boolean returnTicket) {
this.returnTicket = returnTicket;
}
public Assertion validate(final String ticket, final String service)
throws TicketValidationException {
if (returnTicket) {
return new AssertionImpl("rod");
}
throw new BadCredentialsException("As requested from mock");
}
}
// ~ Methods
// ========================================================================================================
private UserDetails makeUserDetails() {
return new User("user", "password", true, true, true, true,
AuthorityUtils.createAuthorityList("ROLE_ONE", "ROLE_TWO"));
}
private UserDetails makeUserDetailsFromAuthoritiesPopulator() {
return new User("user", "password", true, true, true, true,
AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B"));
}
private ServiceProperties makeServiceProperties() {
final ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setSendRenew(false);
serviceProperties.setService("http://test.com");
return serviceProperties;
}
@Test
public void statefulAuthenticationIsSuccessful() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
StatelessTicketCache cache = new MockStatelessTicketCache();
cap.setStatelessTicketCache(cache);
cap.setServiceProperties(makeServiceProperties());
cap.setTicketValidator(new MockTicketValidator(true));
cap.afterPropertiesSet();
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER, "ST-123");
token.setDetails("details");
Authentication result = cap.authenticate(token);
// Confirm ST-123 was NOT added to the cache
assertTrue(cache.getByTicketId("ST-456") == null);
if (!(result instanceof CasAuthenticationToken)) {
fail("Should have returned a CasAuthenticationToken");
}
CasAuthenticationToken casResult = (CasAuthenticationToken) result;
assertEquals(makeUserDetailsFromAuthoritiesPopulator(), casResult.getPrincipal());
assertEquals("ST-123", casResult.getCredentials());
assertTrue(casResult.getAuthorities().contains(
new SimpleGrantedAuthority("ROLE_A")));
assertTrue(casResult.getAuthorities().contains(
new SimpleGrantedAuthority("ROLE_B")));
assertEquals(cap.getKey().hashCode(), casResult.getKeyHash());
assertEquals("details", casResult.getDetails());
// Now confirm the CasAuthenticationToken is automatically re-accepted.
// To ensure TicketValidator not called again, set it to deliver an exception...
cap.setTicketValidator(new MockTicketValidator(false));
Authentication laterResult = cap.authenticate(result);
assertEquals(result, laterResult);
}
@Test
public void statelessAuthenticationIsSuccessful() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
StatelessTicketCache cache = new MockStatelessTicketCache();
cap.setStatelessTicketCache(cache);
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, "ST-456");
token.setDetails("details");
Authentication result = cap.authenticate(token);
// Confirm ST-456 was added to the cache
assertTrue(cache.getByTicketId("ST-456") != null);
if (!(result instanceof CasAuthenticationToken)) {
fail("Should have returned a CasAuthenticationToken");
}
assertEquals(makeUserDetailsFromAuthoritiesPopulator(), result.getPrincipal());
assertEquals("ST-456", result.getCredentials());
assertEquals("details", result.getDetails());
// Now try to authenticate again. To ensure TicketValidator not
// called again, set it to deliver an exception...
cap.setTicketValidator(new MockTicketValidator(false));
// Previously created UsernamePasswordAuthenticationToken is OK
Authentication newResult = cap.authenticate(token);
assertEquals(makeUserDetailsFromAuthoritiesPopulator(), newResult.getPrincipal());
assertEquals("ST-456", newResult.getCredentials());
}
@Test
public void authenticateAllNullService() throws Exception {
String serviceUrl = "https://service/context";
ServiceAuthenticationDetails details = mock(ServiceAuthenticationDetails.class);
when(details.getServiceUrl()).thenReturn(serviceUrl);
TicketValidator validator = mock(TicketValidator.class);
when(validator.validate(any(String.class), any(String.class))).thenReturn(
new AssertionImpl("rod"));
ServiceProperties serviceProperties = makeServiceProperties();
serviceProperties.setAuthenticateAllArtifacts(true);
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setTicketValidator(validator);
cap.setServiceProperties(serviceProperties);
cap.afterPropertiesSet();
String ticket = "ST-456";
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, ticket);
Authentication result = cap.authenticate(token);
}
@Test
public void authenticateAllAuthenticationIsSuccessful() throws Exception {
String serviceUrl = "https://service/context";
ServiceAuthenticationDetails details = mock(ServiceAuthenticationDetails.class);
when(details.getServiceUrl()).thenReturn(serviceUrl);
TicketValidator validator = mock(TicketValidator.class);
when(validator.validate(any(String.class), any(String.class))).thenReturn(
new AssertionImpl("rod"));
ServiceProperties serviceProperties = makeServiceProperties();
serviceProperties.setAuthenticateAllArtifacts(true);
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setTicketValidator(validator);
cap.setServiceProperties(serviceProperties);
cap.afterPropertiesSet();
String ticket = "ST-456";
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, ticket);
Authentication result = cap.authenticate(token);
verify(validator).validate(ticket, serviceProperties.getService());
serviceProperties.setAuthenticateAllArtifacts(true);
result = cap.authenticate(token);
verify(validator, times(2)).validate(ticket, serviceProperties.getService());
token.setDetails(details);
result = cap.authenticate(token);
verify(validator).validate(ticket, serviceUrl);
serviceProperties.setAuthenticateAllArtifacts(false);
serviceProperties.setService(null);
cap.setServiceProperties(serviceProperties);
cap.afterPropertiesSet();
result = cap.authenticate(token);
verify(validator, times(2)).validate(ticket, serviceUrl);
token.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest()));
try {
cap.authenticate(token);
fail("Expected Exception");
}
catch (IllegalStateException success) {
}
cap.setServiceProperties(null);
cap.afterPropertiesSet();
try {
cap.authenticate(token);
fail("Expected Exception");
}
catch (IllegalStateException success) {
}
}
@Test(expected = BadCredentialsException.class)
public void missingTicketIdIsDetected() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
StatelessTicketCache cache = new MockStatelessTicketCache();
cap.setStatelessTicketCache(cache);
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER, "");
cap.authenticate(token);
}
@Test(expected = BadCredentialsException.class)
public void invalidKeyIsDetected() throws Exception {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
StatelessTicketCache cache = new MockStatelessTicketCache();
cap.setStatelessTicketCache(cache);
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
CasAuthenticationToken token = new CasAuthenticationToken("WRONG_KEY",
makeUserDetails(), "credentials",
AuthorityUtils.createAuthorityList("XX"), makeUserDetails(), assertion);
cap.authenticate(token);
}
@Test(expected = IllegalArgumentException.class)
public void detectsMissingAuthoritiesPopulator() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
}
@Test(expected = IllegalArgumentException.class)
public void detectsMissingKey() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setStatelessTicketCache(new MockStatelessTicketCache());
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
}
@Test(expected = IllegalArgumentException.class)
public void detectsMissingStatelessTicketCache() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
// set this explicitly to null to test failure
cap.setStatelessTicketCache(null);
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
}
@Test(expected = IllegalArgumentException.class)
public void detectsMissingTicketValidator() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
}
@Test
public void gettersAndSettersMatch() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
// TODO disabled because why do we need to expose this?
// assertTrue(cap.getUserDetailsService() != null);
assertEquals("qwerty", cap.getKey());
assertTrue(cap.getStatelessTicketCache() != null);
assertTrue(cap.getTicketValidator() != null);
}
@Test
public void ignoresClassesItDoesNotSupport() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
TestingAuthenticationToken token = new TestingAuthenticationToken("user",
"password", "ROLE_A");
assertFalse(cap.supports(TestingAuthenticationToken.class));
// Try it anyway
assertEquals(null, cap.authenticate(token));
}
@Test
public void ignoresUsernamePasswordAuthenticationTokensWithoutCasIdentifiersAsPrincipal()
throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
"some_normal_user", "password",
AuthorityUtils.createAuthorityList("ROLE_A"));
assertEquals(null, cap.authenticate(token));
}
@Test
public void supportsRequiredTokens() {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
assertTrue(cap.supports(UsernamePasswordAuthenticationToken.class));
assertTrue(cap.supports(CasAuthenticationToken.class));
}
// ~ Inner Classes
// ==================================================================================================
private class MockAuthoritiesPopulator implements AuthenticationUserDetailsService {
public UserDetails loadUserDetails(final Authentication token)
throws UsernameNotFoundException {
return makeUserDetailsFromAuthoritiesPopulator();
}
}
private class MockStatelessTicketCache implements StatelessTicketCache {
private Map<String, CasAuthenticationToken> cache = new HashMap<String, CasAuthenticationToken>();
public CasAuthenticationToken getByTicketId(String serviceTicket) {
return cache.get(serviceTicket);
}
public void putTicketInCache(CasAuthenticationToken token) {
cache.put(token.getCredentials().toString(), token);
}
public void removeTicketFromCache(CasAuthenticationToken token) {
throw new UnsupportedOperationException("mock method not implemented");
}
public void removeTicketFromCache(String serviceTicket) {
throw new UnsupportedOperationException("mock method not implemented");
}
}
private class MockTicketValidator implements TicketValidator {
private boolean returnTicket;
public MockTicketValidator(boolean returnTicket) {
this.returnTicket = returnTicket;
}
public Assertion validate(final String ticket, final String service)
throws TicketValidationException {
if (returnTicket) {
return new AssertionImpl("rod");
}
throw new BadCredentialsException("As requested from mock");
}
}
}

View File

@ -33,156 +33,176 @@ import java.util.*;
* @author Ben Alex
*/
public class CasAuthenticationTokenTests extends TestCase {
private final List<GrantedAuthority> ROLES = AuthorityUtils.createAuthorityList("ROLE_ONE","ROLE_TWO");
private final List<GrantedAuthority> ROLES = AuthorityUtils.createAuthorityList(
"ROLE_ONE", "ROLE_TWO");
private UserDetails makeUserDetails() {
return makeUserDetails("user");
}
private UserDetails makeUserDetails() {
return makeUserDetails("user");
}
private UserDetails makeUserDetails(final String name) {
return new User(name, "password", true, true, true, true, ROLES);
}
private UserDetails makeUserDetails(final String name) {
return new User(name, "password", true, true, true, true, ROLES);
}
public final void setUp() throws Exception {
super.setUp();
}
public final void setUp() throws Exception {
super.setUp();
}
public void testConstructorRejectsNulls() {
final Assertion assertion = new AssertionImpl("test");
try {
new CasAuthenticationToken(null, makeUserDetails(), "Password", ROLES, makeUserDetails(), assertion);
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
public void testConstructorRejectsNulls() {
final Assertion assertion = new AssertionImpl("test");
try {
new CasAuthenticationToken(null, makeUserDetails(), "Password", ROLES,
makeUserDetails(), assertion);
fail("Should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
new CasAuthenticationToken("key", null, "Password", ROLES, makeUserDetails(), assertion);
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
try {
new CasAuthenticationToken("key", null, "Password", ROLES, makeUserDetails(),
assertion);
fail("Should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
new CasAuthenticationToken("key", makeUserDetails(), null, ROLES, makeUserDetails(), assertion);
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
try {
new CasAuthenticationToken("key", makeUserDetails(), null, ROLES,
makeUserDetails(), assertion);
fail("Should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES, makeUserDetails(), null);
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
try {
new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
makeUserDetails(), null);
fail("Should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES, null, assertion);
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
try {
new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES, null,
assertion);
fail("Should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
new CasAuthenticationToken("key", makeUserDetails(), "Password", AuthorityUtils.createAuthorityList("ROLE_1", null), makeUserDetails(), assertion);
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
try {
new CasAuthenticationToken("key", makeUserDetails(), "Password",
AuthorityUtils.createAuthorityList("ROLE_1", null),
makeUserDetails(), assertion);
fail("Should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
public void testEqualsWhenEqual() {
final Assertion assertion = new AssertionImpl("test");
public void testEqualsWhenEqual() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
makeUserDetails(), assertion);
CasAuthenticationToken token1 = new CasAuthenticationToken("key",
makeUserDetails(), "Password", ROLES, makeUserDetails(), assertion);
CasAuthenticationToken token2 = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
makeUserDetails(), assertion);
CasAuthenticationToken token2 = new CasAuthenticationToken("key",
makeUserDetails(), "Password", ROLES, makeUserDetails(), assertion);
assertEquals(token1, token2);
}
assertEquals(token1, token2);
}
public void testGetters() {
// Build the proxy list returned in the ticket from CAS
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
makeUserDetails(), assertion);
assertEquals("key".hashCode(), token.getKeyHash());
assertEquals(makeUserDetails(), token.getPrincipal());
assertEquals("Password", token.getCredentials());
assertTrue(token.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_ONE")));
assertTrue(token.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_TWO")));
assertEquals(assertion, token.getAssertion());
assertEquals(makeUserDetails().getUsername(), token.getUserDetails().getUsername());
}
public void testGetters() {
// Build the proxy list returned in the ticket from CAS
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token = new CasAuthenticationToken("key",
makeUserDetails(), "Password", ROLES, makeUserDetails(), assertion);
assertEquals("key".hashCode(), token.getKeyHash());
assertEquals(makeUserDetails(), token.getPrincipal());
assertEquals("Password", token.getCredentials());
assertTrue(token.getAuthorities()
.contains(new SimpleGrantedAuthority("ROLE_ONE")));
assertTrue(token.getAuthorities()
.contains(new SimpleGrantedAuthority("ROLE_TWO")));
assertEquals(assertion, token.getAssertion());
assertEquals(makeUserDetails().getUsername(), token.getUserDetails()
.getUsername());
}
public void testNoArgConstructorDoesntExist() {
try {
CasAuthenticationToken.class.getDeclaredConstructor((Class[]) null);
fail("Should have thrown NoSuchMethodException");
} catch (NoSuchMethodException expected) {
assertTrue(true);
}
}
public void testNoArgConstructorDoesntExist() {
try {
CasAuthenticationToken.class.getDeclaredConstructor((Class[]) null);
fail("Should have thrown NoSuchMethodException");
}
catch (NoSuchMethodException expected) {
assertTrue(true);
}
}
public void testNotEqualsDueToAbstractParentEqualsCheck() {
final Assertion assertion = new AssertionImpl("test");
public void testNotEqualsDueToAbstractParentEqualsCheck() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
makeUserDetails(), assertion);
CasAuthenticationToken token1 = new CasAuthenticationToken("key",
makeUserDetails(), "Password", ROLES, makeUserDetails(), assertion);
CasAuthenticationToken token2 = new CasAuthenticationToken("key", makeUserDetails("OTHER_NAME"), "Password",
ROLES, makeUserDetails(), assertion);
CasAuthenticationToken token2 = new CasAuthenticationToken("key",
makeUserDetails("OTHER_NAME"), "Password", ROLES, makeUserDetails(),
assertion);
assertTrue(!token1.equals(token2));
}
assertTrue(!token1.equals(token2));
}
public void testNotEqualsDueToDifferentAuthenticationClass() {
final Assertion assertion = new AssertionImpl("test");
public void testNotEqualsDueToDifferentAuthenticationClass() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
makeUserDetails(), assertion);
CasAuthenticationToken token1 = new CasAuthenticationToken("key",
makeUserDetails(), "Password", ROLES, makeUserDetails(), assertion);
UsernamePasswordAuthenticationToken token2 = new UsernamePasswordAuthenticationToken("Test", "Password", ROLES);
assertTrue(!token1.equals(token2));
}
UsernamePasswordAuthenticationToken token2 = new UsernamePasswordAuthenticationToken(
"Test", "Password", ROLES);
assertTrue(!token1.equals(token2));
}
public void testNotEqualsDueToKey() {
final Assertion assertion = new AssertionImpl("test");
public void testNotEqualsDueToKey() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
makeUserDetails(), assertion);
CasAuthenticationToken token1 = new CasAuthenticationToken("key",
makeUserDetails(), "Password", ROLES, makeUserDetails(), assertion);
CasAuthenticationToken token2 = new CasAuthenticationToken("DIFFERENT_KEY", makeUserDetails(), "Password",
ROLES, makeUserDetails(), assertion);
CasAuthenticationToken token2 = new CasAuthenticationToken("DIFFERENT_KEY",
makeUserDetails(), "Password", ROLES, makeUserDetails(), assertion);
assertTrue(!token1.equals(token2));
}
assertTrue(!token1.equals(token2));
}
public void testNotEqualsDueToAssertion() {
final Assertion assertion = new AssertionImpl("test");
final Assertion assertion2 = new AssertionImpl("test");
public void testNotEqualsDueToAssertion() {
final Assertion assertion = new AssertionImpl("test");
final Assertion assertion2 = new AssertionImpl("test");
CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
makeUserDetails(), assertion);
CasAuthenticationToken token1 = new CasAuthenticationToken("key",
makeUserDetails(), "Password", ROLES, makeUserDetails(), assertion);
CasAuthenticationToken token2 = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
makeUserDetails(), assertion2);
CasAuthenticationToken token2 = new CasAuthenticationToken("key",
makeUserDetails(), "Password", ROLES, makeUserDetails(), assertion2);
assertTrue(!token1.equals(token2));
}
assertTrue(!token1.equals(token2));
}
public void testSetAuthenticated() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token = new CasAuthenticationToken("key", makeUserDetails(), "Password", ROLES,
makeUserDetails(), assertion);
assertTrue(token.isAuthenticated());
token.setAuthenticated(false);
assertTrue(!token.isAuthenticated());
}
public void testSetAuthenticated() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token = new CasAuthenticationToken("key",
makeUserDetails(), "Password", ROLES, makeUserDetails(), assertion);
assertTrue(token.isAuthenticated());
token.setAuthenticated(false);
assertTrue(!token.isAuthenticated());
}
public void testToString() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token = new CasAuthenticationToken("key", makeUserDetails(), "Password",ROLES,
makeUserDetails(), assertion);
String result = token.toString();
assertTrue(result.lastIndexOf("Credentials (Service/Proxy Ticket):") != -1);
}
public void testToString() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token = new CasAuthenticationToken("key",
makeUserDetails(), "Password", ROLES, makeUserDetails(), assertion);
String result = token.toString();
assertTrue(result.lastIndexOf("Credentials (Service/Proxy Ticket):") != -1);
}
}

View File

@ -27,62 +27,63 @@ import org.springframework.security.cas.authentication.EhCacheBasedTicketCache;
import static org.junit.Assert.*;
/**
* Tests {@link EhCacheBasedTicketCache}.
*
* @author Ben Alex
*/
public class EhCacheBasedTicketCacheTests extends AbstractStatelessTicketCacheTests {
private static CacheManager cacheManager;
private static CacheManager cacheManager;
//~ Methods ========================================================================================================
@BeforeClass
public static void initCacheManaer() {
cacheManager = CacheManager.create();
cacheManager.addCache(new Cache("castickets", 500, false, false, 30, 30));
}
// ~ Methods
// ========================================================================================================
@BeforeClass
public static void initCacheManaer() {
cacheManager = CacheManager.create();
cacheManager.addCache(new Cache("castickets", 500, false, false, 30, 30));
}
@AfterClass
public static void shutdownCacheManager() {
cacheManager.removalAll();
cacheManager.shutdown();
}
@AfterClass
public static void shutdownCacheManager() {
cacheManager.removalAll();
cacheManager.shutdown();
}
@Test
public void testCacheOperation() throws Exception {
EhCacheBasedTicketCache cache = new EhCacheBasedTicketCache();
cache.setCache(cacheManager.getCache("castickets"));
cache.afterPropertiesSet();
@Test
public void testCacheOperation() throws Exception {
EhCacheBasedTicketCache cache = new EhCacheBasedTicketCache();
cache.setCache(cacheManager.getCache("castickets"));
cache.afterPropertiesSet();
final CasAuthenticationToken token = getToken();
final CasAuthenticationToken token = getToken();
// Check it gets stored in the cache
cache.putTicketInCache(token);
assertEquals(token, cache.getByTicketId("ST-0-ER94xMJmn6pha35CQRoZ"));
// Check it gets stored in the cache
cache.putTicketInCache(token);
assertEquals(token, cache.getByTicketId("ST-0-ER94xMJmn6pha35CQRoZ"));
// Check it gets removed from the cache
cache.removeTicketFromCache(getToken());
assertNull(cache.getByTicketId("ST-0-ER94xMJmn6pha35CQRoZ"));
// Check it gets removed from the cache
cache.removeTicketFromCache(getToken());
assertNull(cache.getByTicketId("ST-0-ER94xMJmn6pha35CQRoZ"));
// Check it doesn't return values for null or unknown service tickets
assertNull(cache.getByTicketId(null));
assertNull(cache.getByTicketId("UNKNOWN_SERVICE_TICKET"));
}
// Check it doesn't return values for null or unknown service tickets
assertNull(cache.getByTicketId(null));
assertNull(cache.getByTicketId("UNKNOWN_SERVICE_TICKET"));
}
@Test
public void testStartupDetectsMissingCache() throws Exception {
EhCacheBasedTicketCache cache = new EhCacheBasedTicketCache();
@Test
public void testStartupDetectsMissingCache() throws Exception {
EhCacheBasedTicketCache cache = new EhCacheBasedTicketCache();
try {
cache.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
try {
cache.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
assertTrue(true);
}
Ehcache myCache = cacheManager.getCache("castickets");
cache.setCache(myCache);
assertEquals(myCache, cache.getCache());
}
Ehcache myCache = cacheManager.getCache("castickets");
cache.setCache(myCache);
assertEquals(myCache, cache.getCache());
}
}

View File

@ -14,7 +14,6 @@
*/
package org.springframework.security.cas.authentication;
import org.junit.Test;
import org.springframework.security.cas.authentication.CasAuthenticationToken;
import org.springframework.security.cas.authentication.NullStatelessTicketCache;
@ -30,18 +29,18 @@ import static org.junit.Assert.*;
*/
public class NullStatelessTicketCacheTests extends AbstractStatelessTicketCacheTests {
private StatelessTicketCache cache = new NullStatelessTicketCache();
private StatelessTicketCache cache = new NullStatelessTicketCache();
@Test
public void testGetter() {
assertNull(cache.getByTicketId(null));
assertNull(cache.getByTicketId("test"));
}
@Test
public void testGetter() {
assertNull(cache.getByTicketId(null));
assertNull(cache.getByTicketId("test"));
}
@Test
public void testInsertAndGet() {
final CasAuthenticationToken token = getToken();
cache.putTicketInCache(token);
assertNull(cache.getByTicketId((String) token.getCredentials()));
}
@Test
public void testInsertAndGet() {
final CasAuthenticationToken token = getToken();
cache.putTicketInCache(token);
assertNull(cache.getByTicketId((String) token.getCredentials()));
}
}

Some files were not shown because too many files have changed in this diff Show More