|
@@ -17,6 +17,7 @@ package net.sf.acegisecurity.intercept;
|
|
|
|
|
|
import net.sf.acegisecurity.AccessDecisionManager;
|
|
|
import net.sf.acegisecurity.AccessDeniedException;
|
|
|
+import net.sf.acegisecurity.AfterInvocationManager;
|
|
|
import net.sf.acegisecurity.Authentication;
|
|
|
import net.sf.acegisecurity.AuthenticationCredentialsNotFoundException;
|
|
|
import net.sf.acegisecurity.AuthenticationException;
|
|
@@ -74,7 +75,7 @@ import java.util.Set;
|
|
|
* For an invocation that is secured (there is a
|
|
|
* <code>ConfigAttributeDefinition</code> for the secure object invocation):
|
|
|
*
|
|
|
- * <ol>
|
|
|
+ * <ol type="a">
|
|
|
* <li>
|
|
|
* Authenticate the request against the configured {@link
|
|
|
* AuthenticationManager}, replacing the <code>Authentication</code> object on
|
|
@@ -103,6 +104,11 @@ import java.util.Set;
|
|
|
* object, return the <code>ContextHolder</code> to the object that existed
|
|
|
* after the call to <code>AuthenticationManager</code>.
|
|
|
* </li>
|
|
|
+ * <li>
|
|
|
+ * If an <code>AfterInvocationManager</code> is defined, invoke the invocation
|
|
|
+ * manager and allow it to replace the object due to be returned to the
|
|
|
+ * caller.
|
|
|
+ * </li>
|
|
|
* </ol>
|
|
|
*
|
|
|
* </li>
|
|
@@ -110,7 +116,7 @@ import java.util.Set;
|
|
|
* For an invocation that is public (there is no
|
|
|
* <code>ConfigAttributeDefinition</code> for the secure object invocation):
|
|
|
*
|
|
|
- * <ol>
|
|
|
+ * <ol type="a">
|
|
|
* <li>
|
|
|
* If the <code>ContextHolder</code> contains a <code>SecureContext</code>, set
|
|
|
* the <code>isAuthenticated</code> flag on the <code>Authentication</code>
|
|
@@ -128,9 +134,9 @@ import java.util.Set;
|
|
|
*
|
|
|
* </li>
|
|
|
* <li>
|
|
|
- * Control again returns to the concrete subclass, which will return to the
|
|
|
- * caller any result or exception that occurred when it proceeded with the
|
|
|
- * execution of the secure object.
|
|
|
+ * Control again returns to the concrete subclass, along with the
|
|
|
+ * <code>Object</code> that should be returned to the caller. The subclass
|
|
|
+ * will then return that result or exception to the original caller.
|
|
|
* </li>
|
|
|
* </ol>
|
|
|
* </p>
|
|
@@ -147,6 +153,7 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean,
|
|
|
//~ Instance fields ========================================================
|
|
|
|
|
|
private AccessDecisionManager accessDecisionManager;
|
|
|
+ private AfterInvocationManager afterInvocationManager;
|
|
|
private ApplicationContext context;
|
|
|
private AuthenticationManager authenticationManager;
|
|
|
private RunAsManager runAsManager = new NullRunAsManager();
|
|
@@ -154,11 +161,30 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean,
|
|
|
|
|
|
//~ Methods ================================================================
|
|
|
|
|
|
+ public void setAfterInvocationManager(
|
|
|
+ AfterInvocationManager afterInvocationManager) {
|
|
|
+ this.afterInvocationManager = afterInvocationManager;
|
|
|
+ }
|
|
|
+
|
|
|
+ public AfterInvocationManager getAfterInvocationManager() {
|
|
|
+ return afterInvocationManager;
|
|
|
+ }
|
|
|
+
|
|
|
public void setApplicationContext(ApplicationContext applicationContext)
|
|
|
throws BeansException {
|
|
|
this.context = applicationContext;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Indicates the type of secure objects the subclass will be presenting to
|
|
|
+ * the abstract parent for processing. This is used to ensure
|
|
|
+ * collaborators wired to the <code>AbstractSecurityInterceptor</code> all
|
|
|
+ * support the indicated secure object class.
|
|
|
+ *
|
|
|
+ * @return the type of secure object the subclass provides services for
|
|
|
+ */
|
|
|
+ public abstract Class getSecureObjectClass();
|
|
|
+
|
|
|
public abstract ObjectDefinitionSource obtainObjectDefinitionSource();
|
|
|
|
|
|
public void setAccessDecisionManager(
|
|
@@ -223,51 +249,116 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean,
|
|
|
logger.warn(
|
|
|
"Could not validate configuration attributes as the MethodDefinitionSource did not return a ConfigAttributeDefinition Iterator");
|
|
|
}
|
|
|
+ } else {
|
|
|
+ Set set = new HashSet();
|
|
|
+
|
|
|
+ while (iter.hasNext()) {
|
|
|
+ ConfigAttributeDefinition def = (ConfigAttributeDefinition) iter
|
|
|
+ .next();
|
|
|
+ Iterator attributes = def.getConfigAttributes();
|
|
|
+
|
|
|
+ while (attributes.hasNext()) {
|
|
|
+ ConfigAttribute attr = (ConfigAttribute) attributes
|
|
|
+ .next();
|
|
|
+
|
|
|
+ if (!this.runAsManager.supports(attr)
|
|
|
+ && !this.accessDecisionManager.supports(attr)
|
|
|
+ && ((this.afterInvocationManager == null)
|
|
|
+ || !this.afterInvocationManager.supports(attr))) {
|
|
|
+ set.add(attr);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- return;
|
|
|
+ if (set.size() == 0) {
|
|
|
+ if (logger.isInfoEnabled()) {
|
|
|
+ logger.info("Validated configuration attributes");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw new IllegalArgumentException(
|
|
|
+ "Unsupported configuration attributes: "
|
|
|
+ + set.toString());
|
|
|
+ }
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- Set set = new HashSet();
|
|
|
+ if (getSecureObjectClass() == null) {
|
|
|
+ throw new IllegalArgumentException(
|
|
|
+ "Subclass must provide a non-null response to getSecureObjectClass()");
|
|
|
+ }
|
|
|
|
|
|
- while (iter.hasNext()) {
|
|
|
- ConfigAttributeDefinition def = (ConfigAttributeDefinition) iter
|
|
|
- .next();
|
|
|
- Iterator attributes = def.getConfigAttributes();
|
|
|
+ if (!this.accessDecisionManager.supports(getSecureObjectClass())) {
|
|
|
+ throw new IllegalArgumentException(
|
|
|
+ "AccessDecisionManager does not support secure object class: "
|
|
|
+ + getSecureObjectClass());
|
|
|
+ }
|
|
|
|
|
|
- while (attributes.hasNext()) {
|
|
|
- ConfigAttribute attr = (ConfigAttribute) attributes.next();
|
|
|
+ boolean result = this.obtainObjectDefinitionSource().supports(getSecureObjectClass());
|
|
|
|
|
|
- if (!this.runAsManager.supports(attr)
|
|
|
- && !this.accessDecisionManager.supports(attr)) {
|
|
|
- set.add(attr);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ if (!result) {
|
|
|
+ throw new IllegalArgumentException(
|
|
|
+ "ObjectDefinitionSource does not support secure object class: "
|
|
|
+ + getSecureObjectClass());
|
|
|
+ }
|
|
|
|
|
|
- if (set.size() == 0) {
|
|
|
- if (logger.isInfoEnabled()) {
|
|
|
- logger.info("Validated configuration attributes");
|
|
|
- }
|
|
|
- } else {
|
|
|
- throw new IllegalArgumentException(
|
|
|
- "Unsupported configuration attributes: " + set.toString());
|
|
|
- }
|
|
|
+ if (!this.runAsManager.supports(getSecureObjectClass())) {
|
|
|
+ throw new IllegalArgumentException(
|
|
|
+ "RunAsManager does not support secure object class: "
|
|
|
+ + getSecureObjectClass());
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((this.afterInvocationManager != null)
|
|
|
+ && !this.afterInvocationManager.supports(getSecureObjectClass())) {
|
|
|
+ throw new IllegalArgumentException(
|
|
|
+ "AfterInvocationManager does not support secure object class: "
|
|
|
+ + getSecureObjectClass());
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!this.obtainObjectDefinitionSource().supports(getSecureObjectClass())) {
|
|
|
+ throw new IllegalArgumentException(
|
|
|
+ "ObjectDefinitionSource does not support secure object class: "
|
|
|
+ + getSecureObjectClass());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- protected void afterInvocation(InterceptorStatusToken token) {
|
|
|
+ /**
|
|
|
+ * Completes the work of the <code>AbstractSecurityInterceptor</code> after
|
|
|
+ * the secure object invocation has been complete
|
|
|
+ *
|
|
|
+ * @param token as returned by the {@link #beforeInvocation(Object)}}
|
|
|
+ * method
|
|
|
+ * @param returnedObject any object returned from the secure object
|
|
|
+ * invocation (may be<code>null</code>)
|
|
|
+ *
|
|
|
+ * @return the object the secure object invocation should ultimately return
|
|
|
+ * to its caller (may be <code>null</code>)
|
|
|
+ */
|
|
|
+ protected Object afterInvocation(InterceptorStatusToken token,
|
|
|
+ Object returnedObject) {
|
|
|
if (token == null) {
|
|
|
- return;
|
|
|
+ // public object
|
|
|
+ return returnedObject;
|
|
|
}
|
|
|
|
|
|
- if (logger.isDebugEnabled()) {
|
|
|
- logger.debug("Reverting to original Authentication: "
|
|
|
- + token.getAuthenticated().toString());
|
|
|
+ if (token.isContextHolderRefreshRequired()) {
|
|
|
+ if (logger.isDebugEnabled()) {
|
|
|
+ logger.debug("Reverting to original Authentication: "
|
|
|
+ + token.getAuthentication().toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ SecureContext secureContext = (SecureContext) ContextHolder
|
|
|
+ .getContext();
|
|
|
+ secureContext.setAuthentication(token.getAuthentication());
|
|
|
+ ContextHolder.setContext(secureContext);
|
|
|
}
|
|
|
|
|
|
- SecureContext secureContext = (SecureContext) ContextHolder.getContext();
|
|
|
- secureContext.setAuthentication(token.getAuthenticated());
|
|
|
- ContextHolder.setContext(secureContext);
|
|
|
+ if (afterInvocationManager != null) {
|
|
|
+ returnedObject = afterInvocationManager.decide(token
|
|
|
+ .getAuthentication(), token.getSecureObject(),
|
|
|
+ token.getAttr(), returnedObject);
|
|
|
+ }
|
|
|
+
|
|
|
+ return returnedObject;
|
|
|
}
|
|
|
|
|
|
protected InterceptorStatusToken beforeInvocation(Object object) {
|
|
@@ -275,10 +366,11 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean,
|
|
|
throw new IllegalArgumentException("Object was null");
|
|
|
}
|
|
|
|
|
|
- if (!this.obtainObjectDefinitionSource().supports(object.getClass())) {
|
|
|
+ if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
|
|
|
throw new IllegalArgumentException(
|
|
|
- "ObjectDefinitionSource does not support objects of type "
|
|
|
- + object.getClass());
|
|
|
+ "Security invocation attempted for object " + object
|
|
|
+ + " but AbstractSecurityInterceptor only configured to support secure objects of type: "
|
|
|
+ + getSecureObjectClass());
|
|
|
}
|
|
|
|
|
|
ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource()
|
|
@@ -365,7 +457,8 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean,
|
|
|
"RunAsManager did not change Authentication object");
|
|
|
}
|
|
|
|
|
|
- return null; // no further work post-invocation
|
|
|
+ return new InterceptorStatusToken(authenticated, false, attr,
|
|
|
+ object); // no further work post-invocation
|
|
|
} else {
|
|
|
if (logger.isDebugEnabled()) {
|
|
|
logger.debug("Switching to RunAs Authentication: "
|
|
@@ -375,10 +468,8 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean,
|
|
|
context.setAuthentication(runAs);
|
|
|
ContextHolder.setContext((Context) context);
|
|
|
|
|
|
- InterceptorStatusToken token = new InterceptorStatusToken();
|
|
|
- token.setAuthenticated(authenticated);
|
|
|
-
|
|
|
- return token; // revert to token.Authenticated post-invocation
|
|
|
+ return new InterceptorStatusToken(authenticated, true, attr,
|
|
|
+ object); // revert to token.Authenticated post-invocation
|
|
|
}
|
|
|
} else {
|
|
|
if (logger.isDebugEnabled()) {
|