From 0e46e5307c9085cd8511a6c2b1db3eb01748b2c1 Mon Sep 17 00:00:00 2001 From: Vishal Puri Date: Fri, 6 Jul 2007 13:34:43 +0000 Subject: [PATCH] SEC-271: added Ordered interface to AcessDecisionVoters --- .../AbstractSecurityInterceptor.java | 884 ++++++++++-------- .../vote/AbstractAccessDecisionManager.java | 205 ++-- .../vote/AuthenticatedVoter.java | 17 +- .../org/acegisecurity/vote/RoleVoter.java | 164 ++-- .../acegisecurity/vote/DenyAgainVoter.java | 75 +- 5 files changed, 775 insertions(+), 570 deletions(-) diff --git a/core/src/main/java/org/acegisecurity/intercept/AbstractSecurityInterceptor.java b/core/src/main/java/org/acegisecurity/intercept/AbstractSecurityInterceptor.java index ecb9dc6f75..744ab5c584 100644 --- a/core/src/main/java/org/acegisecurity/intercept/AbstractSecurityInterceptor.java +++ b/core/src/main/java/org/acegisecurity/intercept/AbstractSecurityInterceptor.java @@ -39,8 +39,11 @@ import org.acegisecurity.runas.NullRunAsManager; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; @@ -52,399 +55,504 @@ import org.springframework.util.Assert; import java.util.HashSet; import java.util.Iterator; +import java.util.Map; import java.util.Set; - /** - * Abstract class that implements security interception for secure objects.

The - * AbstractSecurityInterceptor will ensure the proper startup configuration of the security interceptor. - * It will also implement the proper handling of secure object invocations, being: - *

    - *
  1. Obtain the {@link Authentication} object from the {@link SecurityContextHolder}.
  2. - *
  3. Determine if the request relates to a secured or public invocation by looking up the secure object - * request against the {@link ObjectDefinitionSource}.
  4. - *
  5. For an invocation that is secured (there is a ConfigAttributeDefinition for the secure - * object invocation): - *
      - *
    1. If either the {@link org.acegisecurity.Authentication#isAuthenticated()} returns - * false, or the {@link #alwaysReauthenticate} is true, authenticate the request - * against the configured {@link AuthenticationManager}. When authenticated, replace the - * Authentication object on the SecurityContextHolder with the returned value.
    2. - *
    3. Authorize the request against the configured {@link AccessDecisionManager}.
    4. - *
    5. Perform any run-as replacement via the configured {@link RunAsManager}.
    6. - *
    7. Pass control back to the concrete subclass, which will actually proceed with executing the - * object. A {@link InterceptorStatusToken} is returned so that after the subclass has finished proceeding - * with execution of the object, its finally clause can ensure the AbstractSecurityInterceptor - * is re-called and tidies up correctly.
    8. - *
    9. The concrete subclass will re-call the AbstractSecurityInterceptor via the - * {@link #afterInvocation(InterceptorStatusToken, Object)} method.
    10. - *
    11. If the RunAsManager replaced the Authentication object, return - * the SecurityContextHolder to the object that existed after the call to - * AuthenticationManager.
    12. - *
    13. If an AfterInvocationManager is defined, invoke the invocation manager and - * allow it to replace the object due to be returned to the caller.
    14. - *
    - *
  6. - *
  7. For an invocation that is public (there is no ConfigAttributeDefinition for the secure - * object invocation): - *
      - *
    1. As described above, the concrete subclass will be returned an - * InterceptorStatusToken which is subsequently re-presented to the - * AbstractSecurityInterceptor after the secure object has been executed. The - * AbstractSecurityInterceptor will take no further action when its {@link - * #afterInvocation(InterceptorStatusToken, Object)} is called.
    2. - *
    - *
  8. - *
  9. Control again returns to the concrete subclass, along with the Object that should be - * returned to the caller. The subclass will then return that result or exception to the original caller.
  10. - *
- *

- * + * Abstract class that implements security interception for secure objects. + *

+ * The AbstractSecurityInterceptor will ensure the proper startup + * configuration of the security interceptor. It will also implement the proper + * handling of secure object invocations, being: + *

    + *
  1. Obtain the {@link Authentication} object from the + * {@link SecurityContextHolder}.
  2. + *
  3. Determine if the request relates to a secured or public invocation by + * looking up the secure object request against the + * {@link ObjectDefinitionSource}.
  4. + *
  5. For an invocation that is secured (there is a + * ConfigAttributeDefinition for the secure object invocation): + *
      + *
    1. If either the {@link org.acegisecurity.Authentication#isAuthenticated()} + * returns false, or the {@link #alwaysReauthenticate} is + * true, authenticate the request against the configured + * {@link AuthenticationManager}. When authenticated, replace the + * Authentication object on the + * SecurityContextHolder with the returned value.
    2. + *
    3. Authorize the request against the configured + * {@link AccessDecisionManager}.
    4. + *
    5. Perform any run-as replacement via the configured {@link RunAsManager}.
    6. + *
    7. Pass control back to the concrete subclass, which will actually proceed + * with executing the object. A {@link InterceptorStatusToken} is returned so + * that after the subclass has finished proceeding with execution of the object, + * its finally clause can ensure the AbstractSecurityInterceptor + * is re-called and tidies up correctly.
    8. + *
    9. The concrete subclass will re-call the + * AbstractSecurityInterceptor via the + * {@link #afterInvocation(InterceptorStatusToken, Object)} method.
    10. + *
    11. If the RunAsManager replaced the + * Authentication object, return the + * SecurityContextHolder to the object that existed after the + * call to AuthenticationManager.
    12. + *
    13. If an AfterInvocationManager is defined, invoke the + * invocation manager and allow it to replace the object due to be returned to + * the caller.
    14. + *
    + *
  6. + *
  7. For an invocation that is public (there is no + * ConfigAttributeDefinition for the secure object invocation): + *
      + *
    1. As described above, the concrete subclass will be returned an + * InterceptorStatusToken which is subsequently re-presented to + * the AbstractSecurityInterceptor after the secure object has + * been executed. The AbstractSecurityInterceptor will take no + * further action when its {@link #afterInvocation(InterceptorStatusToken, + * Object)} is called.
    2. + *
    + *
  8. + *
  9. Control again returns to the concrete subclass, along with the + * Object that should be returned to the caller. The subclass + * will then return that result or exception to the original caller.
  10. + *
+ *

+ * * @author Ben Alex - * @version $Id$ + * @version $Id: AbstractSecurityInterceptor.java 1790 2007-03-30 18:27:19Z + * luke_t $ */ public abstract class AbstractSecurityInterceptor implements InitializingBean, ApplicationEventPublisherAware, - MessageSourceAware { - //~ Static fields/initializers ===================================================================================== - - protected static final Log logger = LogFactory.getLog(AbstractSecurityInterceptor.class); - - //~ Instance fields ================================================================================================ - - private AccessDecisionManager accessDecisionManager; - private AfterInvocationManager afterInvocationManager; - private ApplicationEventPublisher eventPublisher; - private AuthenticationManager authenticationManager; - protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor(); - private RunAsManager runAsManager = new NullRunAsManager(); - private boolean alwaysReauthenticate = false; - private boolean rejectPublicInvocations = false; - private boolean validateConfigAttributes = true; - - //~ Methods ======================================================================================================== - - /** - * Completes the work of the AbstractSecurityInterceptor 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 benull) - * - * @return the object the secure object invocation should ultimately return to its caller (may be - * null) - */ - protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) { - if (token == null) { - // public object - return returnedObject; - } - - if (token.isContextHolderRefreshRequired()) { - if (logger.isDebugEnabled()) { - logger.debug("Reverting to original Authentication: " + token.getAuthentication().toString()); - } - - SecurityContextHolder.getContext().setAuthentication(token.getAuthentication()); - } - - if (afterInvocationManager != null) { - // Attempt after invocation handling - try { - returnedObject = afterInvocationManager.decide(token.getAuthentication(), token.getSecureObject(), - token.getAttr(), returnedObject); - } catch (AccessDeniedException accessDeniedException) { - AuthorizationFailureEvent event = new AuthorizationFailureEvent(token.getSecureObject(), - token.getAttr(), token.getAuthentication(), accessDeniedException); - publishEvent(event); - - throw accessDeniedException; - } - } - - return returnedObject; - } - - public void afterPropertiesSet() throws Exception { - Assert.notNull(getSecureObjectClass(), "Subclass must provide a non-null response to getSecureObjectClass()"); - - Assert.notNull(this.messages, "A message source must be set"); - Assert.notNull(this.authenticationManager, "An AuthenticationManager is required"); - - Assert.notNull(this.accessDecisionManager, "An AccessDecisionManager is required"); - - Assert.notNull(this.runAsManager, "A RunAsManager is required"); - - Assert.notNull(this.obtainObjectDefinitionSource(), "An ObjectDefinitionSource is required"); - - Assert.isTrue(this.obtainObjectDefinitionSource().supports(getSecureObjectClass()), - "ObjectDefinitionSource does not support secure object class: " + getSecureObjectClass()); - - Assert.isTrue(this.runAsManager.supports(getSecureObjectClass()), - "RunAsManager does not support secure object class: " + getSecureObjectClass()); - - Assert.isTrue(this.accessDecisionManager.supports(getSecureObjectClass()), - "AccessDecisionManager does not support secure object class: " + getSecureObjectClass()); - - if (this.afterInvocationManager != null) { - Assert.isTrue(this.afterInvocationManager.supports(getSecureObjectClass()), - "AfterInvocationManager does not support secure object class: " + getSecureObjectClass()); - } - - if (this.validateConfigAttributes) { - Iterator iter = this.obtainObjectDefinitionSource().getConfigAttributeDefinitions(); - - if (iter == null) { - logger.warn("Could not validate configuration attributes as the MethodDefinitionSource did not return " - + "a ConfigAttributeDefinition Iterator"); - return; - } - - Set unsupportedAttrs = 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))) { - unsupportedAttrs.add(attr); - } - } - } - - if (unsupportedAttrs.size() != 0) { - throw new IllegalArgumentException("Unsupported configuration attributes: " + unsupportedAttrs); - } - - logger.info("Validated configuration attributes"); - } - } - - protected InterceptorStatusToken beforeInvocation(Object object) { - Assert.notNull(object, "Object was null"); - - if (!getSecureObjectClass().isAssignableFrom(object.getClass())) { - throw new IllegalArgumentException("Security invocation attempted for object " - + object.getClass().getName() - + " but AbstractSecurityInterceptor only configured to support secure objects of type: " - + getSecureObjectClass()); - } - - ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource().getAttributes(object); - - if (attr == null) { - if(rejectPublicInvocations) { - throw new IllegalArgumentException( - "No public invocations are allowed via this AbstractSecurityInterceptor. " - + "This indicates a configuration error because the " - + "AbstractSecurityInterceptor.rejectPublicInvocations property is set to 'true'"); - } - - if (logger.isDebugEnabled()) { - logger.debug("Public object - authentication not attempted"); - } - - publishEvent(new PublicInvocationEvent(object)); - - return null; // no further work post-invocation - } - - - if (logger.isDebugEnabled()) { - logger.debug("Secure object: " + object.toString() + "; ConfigAttributes: " + attr.toString()); - } + MessageSourceAware, ApplicationContextAware { + // ~ Static fields/initializers + // ===================================================================================== - if (SecurityContextHolder.getContext().getAuthentication() == null) { - credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound", - "An Authentication object was not found in the SecurityContext"), object, attr); - } - - // Attempt authentication if not already authenticated, or user always wants reauthentication - Authentication authenticated; - - if (!SecurityContextHolder.getContext().getAuthentication().isAuthenticated() || alwaysReauthenticate) { - try { - authenticated = this.authenticationManager.authenticate(SecurityContextHolder.getContext() - .getAuthentication()); - } catch (AuthenticationException authenticationException) { - throw authenticationException; - } - - // We don't authenticated.setAuthentication(true), because each provider should do that - if (logger.isDebugEnabled()) { - logger.debug("Successfully Authenticated: " + authenticated.toString()); - } - - SecurityContextHolder.getContext().setAuthentication(authenticated); - } else { - authenticated = SecurityContextHolder.getContext().getAuthentication(); - - if (logger.isDebugEnabled()) { - logger.debug("Previously Authenticated: " + authenticated.toString()); - } - } - - // Attempt authorization - try { - this.accessDecisionManager.decide(authenticated, object, attr); - } catch (AccessDeniedException accessDeniedException) { - AuthorizationFailureEvent event = new AuthorizationFailureEvent(object, attr, authenticated, - accessDeniedException); - publishEvent(event); - - throw accessDeniedException; - } - - if (logger.isDebugEnabled()) { - logger.debug("Authorization successful"); - } - - AuthorizedEvent event = new AuthorizedEvent(object, attr, authenticated); - publishEvent(event); - - // Attempt to run as a different user - Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attr); - - if (runAs == null) { - if (logger.isDebugEnabled()) { - logger.debug("RunAsManager did not change Authentication object"); - } - - // no further work post-invocation - return new InterceptorStatusToken(authenticated, false, attr, object); - } else { - if (logger.isDebugEnabled()) { - logger.debug("Switching to RunAs Authentication: " + runAs.toString()); - } - - SecurityContextHolder.getContext().setAuthentication(runAs); - - // revert to token.Authenticated post-invocation - return new InterceptorStatusToken(authenticated, true, attr, object); - } - } - - /** - * Helper method which generates an exception containing the passed reason, and publishes an event to the - * application context.

Always throws an exception.

- * - * @param reason to be provided in the exception detail - * @param secureObject that was being called - * @param configAttribs that were defined for the secureObject - */ - private void credentialsNotFound(String reason, Object secureObject, ConfigAttributeDefinition configAttribs) { - AuthenticationCredentialsNotFoundException exception = new AuthenticationCredentialsNotFoundException(reason); - - AuthenticationCredentialsNotFoundEvent event = new AuthenticationCredentialsNotFoundEvent(secureObject, - configAttribs, exception); - publishEvent(event); - - throw exception; - } - - public AccessDecisionManager getAccessDecisionManager() { - return accessDecisionManager; - } - - public AfterInvocationManager getAfterInvocationManager() { - return afterInvocationManager; - } - - public AuthenticationManager getAuthenticationManager() { - return this.authenticationManager; - } - - public RunAsManager getRunAsManager() { - return runAsManager; - } - - /** - * 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 AbstractSecurityInterceptor all - * support the indicated secure object class. - * - * @return the type of secure object the subclass provides services for - */ - public abstract Class getSecureObjectClass(); - - public boolean isAlwaysReauthenticate() { - return alwaysReauthenticate; - } - - public boolean isRejectPublicInvocations() { - return rejectPublicInvocations; - } - - public boolean isValidateConfigAttributes() { - return validateConfigAttributes; - } - - public abstract ObjectDefinitionSource obtainObjectDefinitionSource(); - - public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) { - this.accessDecisionManager = accessDecisionManager; - } - - public void setAfterInvocationManager(AfterInvocationManager afterInvocationManager) { - this.afterInvocationManager = afterInvocationManager; - } - - /** - * Indicates whether the AbstractSecurityInterceptor should ignore the {@link - * Authentication#isAuthenticated()} property. Defaults to false, meaning by default the - * Authentication.isAuthenticated() property is trusted and re-authentication will not occur if the - * principal has already been authenticated. - * - * @param alwaysReauthenticate true to force AbstractSecurityInterceptor to disregard the - * value of Authentication.isAuthenticated() and always re-authenticate the request (defaults - * to false). - */ - public void setAlwaysReauthenticate(boolean alwaysReauthenticate) { - this.alwaysReauthenticate = alwaysReauthenticate; - } - - public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { - this.eventPublisher = applicationEventPublisher; - } - - public void setAuthenticationManager(AuthenticationManager newManager) { - this.authenticationManager = newManager; - } - - public void setMessageSource(MessageSource messageSource) { - this.messages = new MessageSourceAccessor(messageSource); - } - - /** - * By rejecting public invocations (and setting this property to true), essentially you are - * ensuring that every secure object invocation advised by AbstractSecurityInterceptor has a - * configuration attribute defined. This is useful to ensure a "fail safe" mode where undeclared secure objects - * will be rejected and configuration omissions detected early. An IllegalArgumentException will be - * thrown by the AbstractSecurityInterceptor if you set this property to true and an - * attempt is made to invoke a secure object that has no configuration attributes. - * - * @param rejectPublicInvocations set to true to reject invocations of secure objects that have no - * configuration attributes (by default it is false which treats undeclared secure objects as - * "public" or unauthorized) - */ - public void setRejectPublicInvocations(boolean rejectPublicInvocations) { - this.rejectPublicInvocations = rejectPublicInvocations; - } - - public void setRunAsManager(RunAsManager runAsManager) { - this.runAsManager = runAsManager; - } - - public void setValidateConfigAttributes(boolean validateConfigAttributes) { - this.validateConfigAttributes = validateConfigAttributes; - } - - private void publishEvent(ApplicationEvent event) { - if (this.eventPublisher != null) { - this.eventPublisher.publishEvent(event); - } - } + protected static final Log logger = LogFactory.getLog(AbstractSecurityInterceptor.class); + + // ~ Instance fields + // ================================================================================================ + + private AccessDecisionManager accessDecisionManager; + + private AfterInvocationManager afterInvocationManager; + + private ApplicationEventPublisher eventPublisher; + + private AuthenticationManager authenticationManager; + + protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor(); + + private RunAsManager runAsManager = new NullRunAsManager(); + + private boolean alwaysReauthenticate = false; + + private boolean rejectPublicInvocations = false; + + private boolean validateConfigAttributes = true; + + private boolean isSetAuthenticationManagerInvoked = false; + + private boolean isSetAccessDecisionManagerInvoked = false; + + private ApplicationContext applicationContext; + + // ~ Methods + // ======================================================================================================== + + /** + * Completes the work of the AbstractSecurityInterceptor + * 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 benull) + * + * @return the object the secure object invocation should ultimately return + * to its caller (may be null) + */ + protected Object afterInvocation(InterceptorStatusToken token, Object returnedObject) { + if (token == null) { + // public object + return returnedObject; + } + + if (token.isContextHolderRefreshRequired()) { + if (logger.isDebugEnabled()) { + logger.debug("Reverting to original Authentication: " + token.getAuthentication().toString()); + } + + SecurityContextHolder.getContext().setAuthentication(token.getAuthentication()); + } + + if (afterInvocationManager != null) { + // Attempt after invocation handling + try { + returnedObject = afterInvocationManager.decide(token.getAuthentication(), token.getSecureObject(), + token.getAttr(), returnedObject); + } + catch (AccessDeniedException accessDeniedException) { + AuthorizationFailureEvent event = new AuthorizationFailureEvent(token.getSecureObject(), token + .getAttr(), token.getAuthentication(), accessDeniedException); + publishEvent(event); + + throw accessDeniedException; + } + } + + return returnedObject; + } + + public void afterPropertiesSet() throws Exception { + Assert.notNull(getSecureObjectClass(), "Subclass must provide a non-null response to getSecureObjectClass()"); + + Assert.notNull(this.messages, "A message source must be set"); + + if (!isSetAuthenticationManagerInvoked) { + autoDetectAuthenticationManager(); + } + Assert.notNull(this.authenticationManager, "An AuthenticationManager is required"); + + if (!isSetAccessDecisionManagerInvoked) { + autoDetectAccessDecisionManager(); + } + Assert.notNull(this.accessDecisionManager, "An AccessDecisionManager is required"); + + Assert.notNull(this.runAsManager, "A RunAsManager is required"); + + Assert.notNull(this.obtainObjectDefinitionSource(), "An ObjectDefinitionSource is required"); + + Assert.isTrue(this.obtainObjectDefinitionSource().supports(getSecureObjectClass()), + "ObjectDefinitionSource does not support secure object class: " + getSecureObjectClass()); + + Assert.isTrue(this.runAsManager.supports(getSecureObjectClass()), + "RunAsManager does not support secure object class: " + getSecureObjectClass()); + + Assert.isTrue(this.accessDecisionManager.supports(getSecureObjectClass()), + "AccessDecisionManager does not support secure object class: " + getSecureObjectClass()); + + if (this.afterInvocationManager != null) { + Assert.isTrue(this.afterInvocationManager.supports(getSecureObjectClass()), + "AfterInvocationManager does not support secure object class: " + getSecureObjectClass()); + } + + if (this.validateConfigAttributes) { + Iterator iter = this.obtainObjectDefinitionSource().getConfigAttributeDefinitions(); + + if (iter == null) { + logger.warn("Could not validate configuration attributes as the MethodDefinitionSource did not return " + + "a ConfigAttributeDefinition Iterator"); + return; + } + + Set unsupportedAttrs = 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))) { + unsupportedAttrs.add(attr); + } + } + } + + if (unsupportedAttrs.size() != 0) { + throw new IllegalArgumentException("Unsupported configuration attributes: " + unsupportedAttrs); + } + + logger.info("Validated configuration attributes"); + } + } + + /** + * Introspects the Applicationcontext for the single instance + * of AccessDecisionManager. If more than one instance of + * AccessDecisionManager is found, the method uses the first + * one detected. + * + * @param applicationContext to locate the instance + */ + private void autoDetectAccessDecisionManager() { + if (applicationContext != null) { + Map map = applicationContext.getBeansOfType(AccessDecisionManager.class); + if (map.size() > 0) + setAccessDecisionManager((AccessDecisionManager) map.values().iterator().next()); + } + } + + /** + * Introspects the Applicationcontext for the single instance + * of AuthenticationManager. If found invoke + * setAuthenticationManager method by providing the found instance of + * authenticationManager as a method parameter. If more than one instance of + * AuthenticationManager is found, the method throws + * IllegalStateException. + * + * @param applicationContext to locate the instance + */ + private void autoDetectAuthenticationManager() { + if (applicationContext != null) { + Map map = applicationContext.getBeansOfType(AuthenticationManager.class); + if (map.size() > 1) { + throw new IllegalArgumentException( + "More than one AuthenticationManager beans detected please refer to the one using " + + " [ authenticationManager ] " + "property"); + } + else if (map.size() == 1) { + setAuthenticationManager((AuthenticationManager) map.values().iterator().next()); + } + } + + } + + protected InterceptorStatusToken beforeInvocation(Object object) { + Assert.notNull(object, "Object was null"); + + if (!getSecureObjectClass().isAssignableFrom(object.getClass())) { + throw new IllegalArgumentException("Security invocation attempted for object " + + object.getClass().getName() + + " but AbstractSecurityInterceptor only configured to support secure objects of type: " + + getSecureObjectClass()); + } + + ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource().getAttributes(object); + + if (attr == null) { + if (rejectPublicInvocations) { + throw new IllegalArgumentException( + "No public invocations are allowed via this AbstractSecurityInterceptor. " + + "This indicates a configuration error because the " + + "AbstractSecurityInterceptor.rejectPublicInvocations property is set to 'true'"); + } + + if (logger.isDebugEnabled()) { + logger.debug("Public object - authentication not attempted"); + } + + publishEvent(new PublicInvocationEvent(object)); + + return null; // no further work post-invocation + } + + if (logger.isDebugEnabled()) { + logger.debug("Secure object: " + object.toString() + "; ConfigAttributes: " + attr.toString()); + } + + if (SecurityContextHolder.getContext().getAuthentication() == null) { + credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound", + "An Authentication object was not found in the SecurityContext"), object, attr); + } + + // Attempt authentication if not already authenticated, or user always + // wants reauthentication + Authentication authenticated; + + if (!SecurityContextHolder.getContext().getAuthentication().isAuthenticated() || alwaysReauthenticate) { + try { + authenticated = this.authenticationManager.authenticate(SecurityContextHolder.getContext() + .getAuthentication()); + } + catch (AuthenticationException authenticationException) { + throw authenticationException; + } + + // We don't authenticated.setAuthentication(true), because each + // provider should do that + if (logger.isDebugEnabled()) { + logger.debug("Successfully Authenticated: " + authenticated.toString()); + } + + SecurityContextHolder.getContext().setAuthentication(authenticated); + } + else { + authenticated = SecurityContextHolder.getContext().getAuthentication(); + + if (logger.isDebugEnabled()) { + logger.debug("Previously Authenticated: " + authenticated.toString()); + } + } + + // Attempt authorization + try { + this.accessDecisionManager.decide(authenticated, object, attr); + } + catch (AccessDeniedException accessDeniedException) { + AuthorizationFailureEvent event = new AuthorizationFailureEvent(object, attr, authenticated, + accessDeniedException); + publishEvent(event); + + throw accessDeniedException; + } + + if (logger.isDebugEnabled()) { + logger.debug("Authorization successful"); + } + + AuthorizedEvent event = new AuthorizedEvent(object, attr, authenticated); + publishEvent(event); + + // Attempt to run as a different user + Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attr); + + if (runAs == null) { + if (logger.isDebugEnabled()) { + logger.debug("RunAsManager did not change Authentication object"); + } + + // no further work post-invocation + return new InterceptorStatusToken(authenticated, false, attr, object); + } + else { + if (logger.isDebugEnabled()) { + logger.debug("Switching to RunAs Authentication: " + runAs.toString()); + } + + SecurityContextHolder.getContext().setAuthentication(runAs); + + // revert to token.Authenticated post-invocation + return new InterceptorStatusToken(authenticated, true, attr, object); + } + } + + /** + * Helper method which generates an exception containing the passed reason, + * and publishes an event to the application context. + *

+ * Always throws an exception. + *

+ * + * @param reason to be provided in the exception detail + * @param secureObject that was being called + * @param configAttribs that were defined for the secureObject + */ + private void credentialsNotFound(String reason, Object secureObject, ConfigAttributeDefinition configAttribs) { + AuthenticationCredentialsNotFoundException exception = new AuthenticationCredentialsNotFoundException(reason); + + AuthenticationCredentialsNotFoundEvent event = new AuthenticationCredentialsNotFoundEvent(secureObject, + configAttribs, exception); + publishEvent(event); + + throw exception; + } + + public AccessDecisionManager getAccessDecisionManager() { + return accessDecisionManager; + } + + public AfterInvocationManager getAfterInvocationManager() { + return afterInvocationManager; + } + + public AuthenticationManager getAuthenticationManager() { + return this.authenticationManager; + } + + public RunAsManager getRunAsManager() { + return runAsManager; + } + + /** + * 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 AbstractSecurityInterceptor all support the + * indicated secure object class. + * + * @return the type of secure object the subclass provides services for + */ + public abstract Class getSecureObjectClass(); + + public boolean isAlwaysReauthenticate() { + return alwaysReauthenticate; + } + + public boolean isRejectPublicInvocations() { + return rejectPublicInvocations; + } + + public boolean isValidateConfigAttributes() { + return validateConfigAttributes; + } + + public abstract ObjectDefinitionSource obtainObjectDefinitionSource(); + + public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) { + isSetAccessDecisionManagerInvoked = true; + this.accessDecisionManager = accessDecisionManager; + } + + public void setAfterInvocationManager(AfterInvocationManager afterInvocationManager) { + this.afterInvocationManager = afterInvocationManager; + } + + /** + * Indicates whether the AbstractSecurityInterceptor should + * ignore the {@link Authentication#isAuthenticated()} property. Defaults to + * false, meaning by default the + * Authentication.isAuthenticated() property is trusted and + * re-authentication will not occur if the principal has already been + * authenticated. + * + * @param alwaysReauthenticate true to force + * AbstractSecurityInterceptor to disregard the value of + * Authentication.isAuthenticated() and always + * re-authenticate the request (defaults to false). + */ + public void setAlwaysReauthenticate(boolean alwaysReauthenticate) { + this.alwaysReauthenticate = alwaysReauthenticate; + } + + public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { + this.eventPublisher = applicationEventPublisher; + } + + public void setAuthenticationManager(AuthenticationManager newManager) { + isSetAuthenticationManagerInvoked = true; + this.authenticationManager = newManager; + } + + public void setMessageSource(MessageSource messageSource) { + this.messages = new MessageSourceAccessor(messageSource); + } + + /** + * By rejecting public invocations (and setting this property to + * true), essentially you are ensuring that every secure + * object invocation advised by AbstractSecurityInterceptor + * has a configuration attribute defined. This is useful to ensure a "fail + * safe" mode where undeclared secure objects will be rejected and + * configuration omissions detected early. An + * IllegalArgumentException will be thrown by the + * AbstractSecurityInterceptor if you set this property to + * true and an attempt is made to invoke a secure object that + * has no configuration attributes. + * + * @param rejectPublicInvocations set to true to reject + * invocations of secure objects that have no configuration attributes (by + * default it is false which treats undeclared secure objects + * as "public" or unauthorized) + */ + public void setRejectPublicInvocations(boolean rejectPublicInvocations) { + this.rejectPublicInvocations = rejectPublicInvocations; + } + + public void setRunAsManager(RunAsManager runAsManager) { + this.runAsManager = runAsManager; + } + + public void setValidateConfigAttributes(boolean validateConfigAttributes) { + this.validateConfigAttributes = validateConfigAttributes; + } + + private void publishEvent(ApplicationEvent event) { + if (this.eventPublisher != null) { + this.eventPublisher.publishEvent(event); + } + } + + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } } diff --git a/core/src/main/java/org/acegisecurity/vote/AbstractAccessDecisionManager.java b/core/src/main/java/org/acegisecurity/vote/AbstractAccessDecisionManager.java index 2854291c23..3b8291d7d7 100644 --- a/core/src/main/java/org/acegisecurity/vote/AbstractAccessDecisionManager.java +++ b/core/src/main/java/org/acegisecurity/vote/AbstractAccessDecisionManager.java @@ -15,126 +15,159 @@ package org.acegisecurity.vote; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + import org.acegisecurity.AccessDecisionManager; import org.acegisecurity.AccessDeniedException; import org.acegisecurity.AcegiMessageSource; import org.acegisecurity.ConfigAttribute; - +import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; - +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.context.MessageSource; import org.springframework.context.MessageSourceAware; import org.springframework.context.support.MessageSourceAccessor; - +import org.springframework.core.OrderComparator; +import org.springframework.core.Ordered; import org.springframework.util.Assert; -import java.util.Iterator; -import java.util.List; - - /** - * Abstract implementation of {@link AccessDecisionManager}.

Handles configuration of a bean context defined list - * of {@link AccessDecisionVoter}s and the access control behaviour if all voters abstain from voting (defaults to - * deny access).

+ * Abstract implementation of {@link AccessDecisionManager}. + *

+ * Handles configuration of a bean context defined list of + * {@link AccessDecisionVoter}s and the access control behaviour if all voters + * abstain from voting (defaults to deny access). + *

*/ public abstract class AbstractAccessDecisionManager implements AccessDecisionManager, InitializingBean, - MessageSourceAware { - //~ Instance fields ================================================================================================ + MessageSourceAware, ApplicationContextAware { + // ~ Instance fields + // ================================================================================================ - private List decisionVoters; - protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor(); - private boolean allowIfAllAbstainDecisions = false; + private List decisionVoters; - //~ Methods ======================================================================================================== + protected MessageSourceAccessor messages = AcegiMessageSource.getAccessor(); - public void afterPropertiesSet() throws Exception { - checkIfValidList(this.decisionVoters); - Assert.notNull(this.messages, "A message source must be set"); - } + private boolean allowIfAllAbstainDecisions = false; - protected final void checkAllowIfAllAbstainDecisions() { - if (!this.isAllowIfAllAbstainDecisions()) { - throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied", - "Access is denied")); - } - } + private boolean isSetDecisionVotersInvoked = false; - private void checkIfValidList(List listToCheck) { - if ((listToCheck == null) || (listToCheck.size() == 0)) { - throw new IllegalArgumentException("A list of AccessDecisionVoters is required"); - } - } + private ApplicationContext applicationContext; - public List getDecisionVoters() { - return this.decisionVoters; - } + // ~ Methods + // ======================================================================================================== - public boolean isAllowIfAllAbstainDecisions() { - return allowIfAllAbstainDecisions; - } + public void afterPropertiesSet() throws Exception { + if (!isSetDecisionVotersInvoked) { + autoDetectVoters(); + } + Assert.notEmpty(this.decisionVoters, "A list of AccessDecisionVoters is required"); + Assert.notNull(this.messages, "A message source must be set"); + } - public void setAllowIfAllAbstainDecisions(boolean allowIfAllAbstainDecisions) { - this.allowIfAllAbstainDecisions = allowIfAllAbstainDecisions; - } + private void autoDetectVoters() { + Map map = this.applicationContext.getBeansOfType(AccessDecisionVoter.class); + List list = new ArrayList(); + for(Iterator it = map.values().iterator(); it.hasNext();) { + list.add((it.next())); + } + Collections.sort(list, new OrderComparator()); + setDecisionVoters(list); + } - public void setDecisionVoters(List newList) { - checkIfValidList(newList); + protected final void checkAllowIfAllAbstainDecisions() { + if (!this.isAllowIfAllAbstainDecisions()) { + throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied", + "Access is denied")); + } + } - Iterator iter = newList.iterator(); + public List getDecisionVoters() { + return this.decisionVoters; + } - while (iter.hasNext()) { - Object currentObject = null; + public boolean isAllowIfAllAbstainDecisions() { + return allowIfAllAbstainDecisions; + } - try { - currentObject = iter.next(); + public void setAllowIfAllAbstainDecisions(boolean allowIfAllAbstainDecisions) { + this.allowIfAllAbstainDecisions = allowIfAllAbstainDecisions; + } - AccessDecisionVoter attemptToCast = (AccessDecisionVoter) currentObject; - } catch (ClassCastException cce) { - throw new IllegalArgumentException("AccessDecisionVoter " + currentObject.getClass().getName() - + " must implement AccessDecisionVoter"); - } - } + public void setDecisionVoters(List newList) { + isSetDecisionVotersInvoked = true; + Assert.notEmpty(newList); - this.decisionVoters = newList; - } + Iterator iter = newList.iterator(); - public void setMessageSource(MessageSource messageSource) { - this.messages = new MessageSourceAccessor(messageSource); - } + while (iter.hasNext()) { + Object currentObject = null; - public boolean supports(ConfigAttribute attribute) { - Iterator iter = this.decisionVoters.iterator(); + try { + currentObject = iter.next(); - while (iter.hasNext()) { - AccessDecisionVoter voter = (AccessDecisionVoter) iter.next(); + AccessDecisionVoter attemptToCast = (AccessDecisionVoter) currentObject; + } + catch (ClassCastException cce) { + throw new IllegalArgumentException("AccessDecisionVoter " + currentObject.getClass().getName() + + " must implement AccessDecisionVoter"); + } + } - if (voter.supports(attribute)) { - return true; - } - } + this.decisionVoters = newList; + } - return false; - } + public void setMessageSource(MessageSource messageSource) { + this.messages = new MessageSourceAccessor(messageSource); + } - /** - * Iterates through all AccessDecisionVoters and ensures each can support the presented class.

If - * one or more voters cannot support the presented class, false is returned.

- * - * @param clazz DOCUMENT ME! - * - * @return DOCUMENT ME! - */ - public boolean supports(Class clazz) { - Iterator iter = this.decisionVoters.iterator(); + public boolean supports(ConfigAttribute attribute) { + Iterator iter = this.decisionVoters.iterator(); - while (iter.hasNext()) { - AccessDecisionVoter voter = (AccessDecisionVoter) iter.next(); + while (iter.hasNext()) { + AccessDecisionVoter voter = (AccessDecisionVoter) iter.next(); - if (!voter.supports(clazz)) { - return false; - } - } + if (voter.supports(attribute)) { + return true; + } + } + + return false; + } + + /** + * Iterates through all AccessDecisionVoters and ensures + * each can support the presented class. + *

+ * If one or more voters cannot support the presented class, + * false is returned. + *

+ * + * @param clazz DOCUMENT ME! + * + * @return DOCUMENT ME! + */ + public boolean supports(Class clazz) { + Iterator iter = this.decisionVoters.iterator(); + + while (iter.hasNext()) { + AccessDecisionVoter voter = (AccessDecisionVoter) iter.next(); + + if (!voter.supports(clazz)) { + return false; + } + } + + return true; + } + + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } - return true; - } } diff --git a/core/src/main/java/org/acegisecurity/vote/AuthenticatedVoter.java b/core/src/main/java/org/acegisecurity/vote/AuthenticatedVoter.java index 2e16b0d6de..1788dace24 100644 --- a/core/src/main/java/org/acegisecurity/vote/AuthenticatedVoter.java +++ b/core/src/main/java/org/acegisecurity/vote/AuthenticatedVoter.java @@ -21,6 +21,7 @@ import org.acegisecurity.AuthenticationTrustResolverImpl; import org.acegisecurity.ConfigAttribute; import org.acegisecurity.ConfigAttributeDefinition; +import org.springframework.core.Ordered; import org.springframework.util.Assert; import java.util.Iterator; @@ -41,16 +42,19 @@ import java.util.Iterator; * @author Ben Alex * @version $Id$ */ -public class AuthenticatedVoter implements AccessDecisionVoter { +public class AuthenticatedVoter implements AccessDecisionVoter, Ordered { //~ Static fields/initializers ===================================================================================== public static final String IS_AUTHENTICATED_FULLY = "IS_AUTHENTICATED_FULLY"; public static final String IS_AUTHENTICATED_REMEMBERED = "IS_AUTHENTICATED_REMEMBERED"; public static final String IS_AUTHENTICATED_ANONYMOUSLY = "IS_AUTHENTICATED_ANONYMOUSLY"; - + public static int DEFAULT_ORDER = Ordered.LOWEST_PRECEDENCE; //~ Instance fields ================================================================================================ private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl(); + + private int order = DEFAULT_ORDER; + //~ Methods ======================================================================================================== @@ -120,4 +124,13 @@ public class AuthenticatedVoter implements AccessDecisionVoter { return result; } + + public void setOrder(int order) { + this.order = order; + } + + public int getOrder() { + return order; + } + } diff --git a/core/src/main/java/org/acegisecurity/vote/RoleVoter.java b/core/src/main/java/org/acegisecurity/vote/RoleVoter.java index eedd85aebb..1bbff6f0f3 100644 --- a/core/src/main/java/org/acegisecurity/vote/RoleVoter.java +++ b/core/src/main/java/org/acegisecurity/vote/RoleVoter.java @@ -18,89 +18,121 @@ package org.acegisecurity.vote; import org.acegisecurity.Authentication; import org.acegisecurity.ConfigAttribute; import org.acegisecurity.ConfigAttributeDefinition; +import org.springframework.core.Ordered; import java.util.Iterator; - /** - *

Votes if any {@link ConfigAttribute#getAttribute()} starts with a prefix indicating that it is a role. The - * default prefix string is ROLE_, but this may be overriden to any value. It may also be set to empty, - * which means that essentially any attribute will be voted on. As described further below, the effect of an empty - * prefix may not be quite desireable.

- *

Abstains from voting if no configuration attribute commences with the role prefix. Votes to grant access if - * there is an exact matching {@link org.acegisecurity.GrantedAuthority} to a ConfigAttribute starting - * with the role prefix. Votes to deny access if there is no exact matching GrantedAuthority to a - * ConfigAttribute starting with the role prefix.

- *

An empty role prefix means that the voter will vote for every ConfigAttribute. When there are different - * categories of ConfigAttributes used, this will not be optimal since the voter will be voting for attributes which - * do not represent roles. However, this option may be of some use when using preexisting role names without a prefix, - * and no ability exists to prefix them with a role prefix on reading them in, such as provided for example in {@link - * org.acegisecurity.userdetails.jdbc.JdbcDaoImpl}.

- *

All comparisons and prefixes are case sensitive.

- * + *

+ * Votes if any {@link ConfigAttribute#getAttribute()} starts with a prefix + * indicating that it is a role. The default prefix string is ROLE_, + * but this may be overriden to any value. It may also be set to empty, which + * means that essentially any attribute will be voted on. As described further + * below, the effect of an empty prefix may not be quite desireable. + *

+ *

+ * Abstains from voting if no configuration attribute commences with the role + * prefix. Votes to grant access if there is an exact matching + * {@link org.acegisecurity.GrantedAuthority} to a ConfigAttribute + * starting with the role prefix. Votes to deny access if there is no exact + * matching GrantedAuthority to a ConfigAttribute + * starting with the role prefix. + *

+ *

+ * An empty role prefix means that the voter will vote for every + * ConfigAttribute. When there are different categories of ConfigAttributes + * used, this will not be optimal since the voter will be voting for attributes + * which do not represent roles. However, this option may be of some use when + * using preexisting role names without a prefix, and no ability exists to + * prefix them with a role prefix on reading them in, such as provided for + * example in {@link org.acegisecurity.userdetails.jdbc.JdbcDaoImpl}. + *

+ *

+ * All comparisons and prefixes are case sensitive. + *

+ * * @author Ben Alex * @author colin sampaleanu * @version $Id$ */ -public class RoleVoter implements AccessDecisionVoter { - //~ Instance fields ================================================================================================ +public class RoleVoter implements AccessDecisionVoter, Ordered { + // ~ Static fields/initializers + // ===================================================================================== + public static int DEFAULT_ORDER = Ordered.LOWEST_PRECEDENCE; - private String rolePrefix = "ROLE_"; + // ~ Instance fields + // ================================================================================================ - //~ Methods ======================================================================================================== + private String rolePrefix = "ROLE_"; - public String getRolePrefix() { - return rolePrefix; - } + private int order = DEFAULT_ORDER; - /** - * Allows the default role prefix of ROLE_ to be overriden. May be set to an empty value, - * although this is usually not desireable. - * - * @param rolePrefix the new prefix - */ - public void setRolePrefix(String rolePrefix) { - this.rolePrefix = rolePrefix; - } + // ~ Methods + // ======================================================================================================== - public boolean supports(ConfigAttribute attribute) { - if ((attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getRolePrefix())) { - return true; - } else { - return false; - } - } + public String getRolePrefix() { + return rolePrefix; + } - /** - * This implementation supports any type of class, because it does not query the presented secure object. - * - * @param clazz the secure object - * - * @return always true - */ - public boolean supports(Class clazz) { - return true; - } + /** + * Allows the default role prefix of ROLE_ to be overriden. + * May be set to an empty value, although this is usually not desireable. + * + * @param rolePrefix the new prefix + */ + public void setRolePrefix(String rolePrefix) { + this.rolePrefix = rolePrefix; + } - public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) { - int result = ACCESS_ABSTAIN; - Iterator iter = config.getConfigAttributes(); + public boolean supports(ConfigAttribute attribute) { + if ((attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getRolePrefix())) { + return true; + } + else { + return false; + } + } - while (iter.hasNext()) { - ConfigAttribute attribute = (ConfigAttribute) iter.next(); + /** + * This implementation supports any type of class, because it does not query + * the presented secure object. + * + * @param clazz the secure object + * + * @return always true + */ + public boolean supports(Class clazz) { + return true; + } - if (this.supports(attribute)) { - result = ACCESS_DENIED; + public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) { + int result = ACCESS_ABSTAIN; + Iterator iter = config.getConfigAttributes(); - // Attempt to find a matching granted authority - for (int i = 0; i < authentication.getAuthorities().length; i++) { - if (attribute.getAttribute().equals(authentication.getAuthorities()[i].getAuthority())) { - return ACCESS_GRANTED; - } - } - } - } + while (iter.hasNext()) { + ConfigAttribute attribute = (ConfigAttribute) iter.next(); + + if (this.supports(attribute)) { + result = ACCESS_DENIED; + + // Attempt to find a matching granted authority + for (int i = 0; i < authentication.getAuthorities().length; i++) { + if (attribute.getAttribute().equals(authentication.getAuthorities()[i].getAuthority())) { + return ACCESS_GRANTED; + } + } + } + } + + return result; + } + + public void setOrder(int order) { + this.order = order; + } + + public int getOrder() { + return order; + } - return result; - } } diff --git a/core/src/test/java/org/acegisecurity/vote/DenyAgainVoter.java b/core/src/test/java/org/acegisecurity/vote/DenyAgainVoter.java index ace32801ce..888aa5ede7 100644 --- a/core/src/test/java/org/acegisecurity/vote/DenyAgainVoter.java +++ b/core/src/test/java/org/acegisecurity/vote/DenyAgainVoter.java @@ -18,45 +18,64 @@ package org.acegisecurity.vote; import org.acegisecurity.Authentication; import org.acegisecurity.ConfigAttribute; import org.acegisecurity.ConfigAttributeDefinition; +import org.springframework.core.Ordered; import java.util.Iterator; - /** - * Implementation of an {@link AccessDecisionVoter} for unit testing.

If the {@link - * ConfigAttribute#getAttribute()} has a value of DENY_AGAIN_FOR_SURE, the voter will vote to deny - * access.

- *

All comparisons are case sensitive.

- * + * Implementation of an {@link AccessDecisionVoter} for unit testing. + *

+ * If the {@link ConfigAttribute#getAttribute()} has a value of + * DENY_AGAIN_FOR_SURE, the voter will vote to deny access. + *

+ *

+ * All comparisons are case sensitive. + *

+ * * @author Ben Alex * @version $Id$ */ -public class DenyAgainVoter implements AccessDecisionVoter { - //~ Methods ======================================================================================================== +public class DenyAgainVoter implements AccessDecisionVoter, Ordered { + public static int DEFAULT_ORDER = Ordered.LOWEST_PRECEDENCE; - public boolean supports(ConfigAttribute attribute) { - if ("DENY_AGAIN_FOR_SURE".equals(attribute.getAttribute())) { - return true; - } else { - return false; - } - } + private int order = DEFAULT_ORDER; - public boolean supports(Class clazz) { - return true; - } + // ~ Methods + // ======================================================================================================== - public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) { - Iterator iter = config.getConfigAttributes(); + public boolean supports(ConfigAttribute attribute) { + if ("DENY_AGAIN_FOR_SURE".equals(attribute.getAttribute())) { + return true; + } + else { + return false; + } + } - while (iter.hasNext()) { - ConfigAttribute attribute = (ConfigAttribute) iter.next(); + public boolean supports(Class clazz) { + return true; + } - if (this.supports(attribute)) { - return ACCESS_DENIED; - } - } + public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) { + Iterator iter = config.getConfigAttributes(); + + while (iter.hasNext()) { + ConfigAttribute attribute = (ConfigAttribute) iter.next(); + + if (this.supports(attribute)) { + return ACCESS_DENIED; + } + } + + return ACCESS_ABSTAIN; + } + + public void setOrder(int order) { + this.order = order; + } + + public int getOrder() { + return order; + } - return ACCESS_ABSTAIN; - } }