|
@@ -35,29 +35,36 @@ import org.springframework.security.acls.sid.SidRetrievalStrategyImpl;
|
|
|
import org.apache.commons.logging.Log;
|
|
|
import org.apache.commons.logging.LogFactory;
|
|
|
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}.</p>
|
|
|
- * <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.</p>
|
|
|
- * <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(org.springframework.security.acls.Permission[],
|
|
|
- * org.springframework.security.acls.sid.Sid[], boolean)} when presenting the {@link #requirePermission} array to that method.</p>
|
|
|
- * <p>If the method argument is <code>null</code>, the voter will abstain from voting. If the method argument
|
|
|
- * could not be found, an {@link org.springframework.security.AuthorizationServiceException} will be thrown.</p>
|
|
|
- * <p>In practical terms users will typically setup a number of <code>AclEntryVoter</code>s. Each will have a
|
|
|
+ * <p>
|
|
|
+ * 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.
|
|
|
+ * <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(org.springframework.security.acls.Permission[],
|
|
|
+ * org.springframework.security.acls.sid.Sid[], 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 org.springframework.security.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 #processDomainObjectClass}, {@link #processConfigAttribute} and {@link #requirePermission}
|
|
|
- * combination. For example, a small application might employ the following instances of <code>AclEntryVoter</code>:
|
|
|
+ * 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>
|
|
|
+ * <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
|
|
@@ -113,18 +120,18 @@ public class AclEntryVoter extends AbstractAclVoter {
|
|
|
* should be invoked to obtain an <code>Object</code> which will be the domain object used for ACL
|
|
|
* evaluation
|
|
|
*/
|
|
|
- public String getInternalMethod() {
|
|
|
+ protected String getInternalMethod() {
|
|
|
return internalMethod;
|
|
|
}
|
|
|
|
|
|
- public String getProcessConfigAttribute() {
|
|
|
- return processConfigAttribute;
|
|
|
- }
|
|
|
-
|
|
|
public void setInternalMethod(String internalMethod) {
|
|
|
this.internalMethod = internalMethod;
|
|
|
}
|
|
|
|
|
|
+ protected String getProcessConfigAttribute() {
|
|
|
+ return processConfigAttribute;
|
|
|
+ }
|
|
|
+
|
|
|
public void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
|
|
|
Assert.notNull(objectIdentityRetrievalStrategy, "ObjectIdentityRetrievalStrategy required");
|
|
|
this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
|
|
@@ -149,95 +156,84 @@ public class AclEntryVoter extends AbstractAclVoter {
|
|
|
while (iter.hasNext()) {
|
|
|
ConfigAttribute attr = (ConfigAttribute) iter.next();
|
|
|
|
|
|
- if (this.supports(attr)) {
|
|
|
- // Need to make an access decision on this invocation
|
|
|
- // Attempt to locate the domain object instance to process
|
|
|
- Object domainObject = getDomainObjectInstance(object);
|
|
|
-
|
|
|
- // Evaluate if we are required to use an inner domain object
|
|
|
- if (domainObject != null && internalMethod != null && (!"".equals(internalMethod))) {
|
|
|
- try {
|
|
|
- Class clazz = domainObject.getClass();
|
|
|
- Method method = clazz.getMethod(internalMethod, new Class[] {});
|
|
|
- domainObject = method.invoke(domainObject, new Object[] {});
|
|
|
- } catch (NoSuchMethodException nsme) {
|
|
|
- throw new AuthorizationServiceException("Object of class '" + domainObject.getClass()
|
|
|
- + "' does not provide the requested internalMethod: " + internalMethod);
|
|
|
- } catch (IllegalAccessException iae) {
|
|
|
- if (logger.isDebugEnabled()) {
|
|
|
- logger.debug("IllegalAccessException", iae);
|
|
|
-
|
|
|
- if (iae.getCause() != null) {
|
|
|
- logger.debug("Cause: " + iae.getCause().getMessage(), iae.getCause());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- throw new AuthorizationServiceException("Problem invoking internalMethod: " + internalMethod
|
|
|
- + " for object: " + domainObject);
|
|
|
- } catch (InvocationTargetException ite) {
|
|
|
- if (logger.isDebugEnabled()) {
|
|
|
- logger.debug("InvocationTargetException", ite);
|
|
|
-
|
|
|
- if (ite.getCause() != null) {
|
|
|
- logger.debug("Cause: " + ite.getCause().getMessage(), ite.getCause());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- throw new AuthorizationServiceException("Problem invoking internalMethod: " + internalMethod
|
|
|
- + " for object: " + domainObject);
|
|
|
- }
|
|
|
+ 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 AccessDecisionVoter.ACCESS_ABSTAIN;
|
|
|
+ }
|
|
|
|
|
|
- return AccessDecisionVoter.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, new Object[0]);
|
|
|
+ } 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);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- // Obtain the OID applicable to the domain object
|
|
|
- ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy.getObjectIdentity(domainObject);
|
|
|
-
|
|
|
- // Obtain the SIDs applicable to the principal
|
|
|
- Sid[] sids = sidRetrievalStrategy.getSids(authentication);
|
|
|
+ // Obtain the OID applicable to the domain object
|
|
|
+ ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy.getObjectIdentity(domainObject);
|
|
|
|
|
|
- Acl acl;
|
|
|
+ // Obtain the SIDs applicable to the principal
|
|
|
+ Sid[] sids = sidRetrievalStrategy.getSids(authentication);
|
|
|
|
|
|
- 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");
|
|
|
- }
|
|
|
+ Acl acl;
|
|
|
|
|
|
- return AccessDecisionVoter.ACCESS_DENIED;
|
|
|
+ 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 {
|
|
|
- if (acl.isGranted(requirePermission, sids, false)) {
|
|
|
- if (logger.isDebugEnabled()) {
|
|
|
- logger.debug("Voting to grant access");
|
|
|
- }
|
|
|
-
|
|
|
- return AccessDecisionVoter.ACCESS_GRANTED;
|
|
|
- } else {
|
|
|
- if (logger.isDebugEnabled()) {
|
|
|
- logger.debug(
|
|
|
- "Voting to deny access - ACLs returned, but insufficient permissions for this principal");
|
|
|
- }
|
|
|
-
|
|
|
- return AccessDecisionVoter.ACCESS_DENIED;
|
|
|
+ return AccessDecisionVoter.ACCESS_DENIED;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (acl.isGranted(requirePermission, sids, false)) {
|
|
|
+ if (logger.isDebugEnabled()) {
|
|
|
+ logger.debug("Voting to grant access");
|
|
|
}
|
|
|
- } catch (NotFoundException nfe) {
|
|
|
+
|
|
|
+ return AccessDecisionVoter.ACCESS_GRANTED;
|
|
|
+ } else {
|
|
|
if (logger.isDebugEnabled()) {
|
|
|
- logger.debug("Voting to deny access - no ACLs apply for this principal");
|
|
|
+ logger.debug(
|
|
|
+ "Voting to deny access - ACLs returned, but insufficient permissions for this principal");
|
|
|
}
|
|
|
|
|
|
return AccessDecisionVoter.ACCESS_DENIED;
|
|
|
}
|
|
|
+ } catch (NotFoundException nfe) {
|
|
|
+ if (logger.isDebugEnabled()) {
|
|
|
+ logger.debug("Voting to deny access - no ACLs apply for this principal");
|
|
|
+ }
|
|
|
+
|
|
|
+ return AccessDecisionVoter.ACCESS_DENIED;
|
|
|
}
|
|
|
}
|
|
|
|