@ -132,6 +132,9 @@ import org.springframework.util.Assert;
@@ -132,6 +132,9 @@ import org.springframework.util.Assert;
* < / li >
* < / ol >
* < / p >
*
* @author Ben Alex
* @version $Id$
* /
public abstract class AbstractSecurityInterceptor implements InitializingBean ,
ApplicationEventPublisherAware , MessageSourceAware {
@ -189,342 +192,342 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean,
@@ -189,342 +192,342 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean,
}
return returnedObject ;
}
}
public void afterPropertiesSet ( ) throws Exception {
Assert . notNull ( getSecureObjectClass ( ) ,
"Subclass must provide a non-null response to getSecureObjectClass()" ) ;
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 . 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 . accessDecisionManager ,
"An AccessDecisionManager is required" ) ;
Assert . notNull ( this . runAsManager , "A RunAsManager is required" ) ;
Assert . notNull ( this . runAsManager , "A RunAsManager is required" ) ;
Assert . notNull ( this . obtainObjectDefinitionSource ( ) ,
"An ObjectDefinitionSource is required" ) ;
Assert . notNull ( this . obtainObjectDefinitionSource ( ) ,
"An ObjectDefinitionSource is required" ) ;
if ( ! this . obtainObjectDefinitionSource ( )
. supports ( getSecureObjectClass ( ) ) ) {
throw new IllegalArgumentException (
"ObjectDefinitionSource does not support secure object class: "
+ getSecureObjectClass ( ) ) ;
}
if ( ! this . obtainObjectDefinitionSource ( )
. supports ( getSecureObjectClass ( ) ) ) {
throw new IllegalArgumentException (
"ObjectDefinitionSource does not support secure object class: "
+ getSecureObjectClass ( ) ) ;
}
if ( ! this . runAsManager . supports ( getSecureObjectClass ( ) ) ) {
throw new IllegalArgumentException (
"RunAsManager does not support secure object class: "
+ getSecureObjectClass ( ) ) ;
}
if ( ! this . runAsManager . supports ( getSecureObjectClass ( ) ) ) {
throw new IllegalArgumentException (
"RunAsManager does not support secure object class: "
+ getSecureObjectClass ( ) ) ;
}
if ( ! this . accessDecisionManager . supports ( getSecureObjectClass ( ) ) ) {
throw new IllegalArgumentException (
"AccessDecisionManager does not support secure object class: "
+ getSecureObjectClass ( ) ) ;
}
if ( ! this . accessDecisionManager . supports ( getSecureObjectClass ( ) ) ) {
throw new IllegalArgumentException (
"AccessDecisionManager 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 . afterInvocationManager ! = null )
& & ! this . afterInvocationManager . supports ( getSecureObjectClass ( ) ) ) {
throw new IllegalArgumentException (
"AfterInvocationManager does not support secure object class: "
+ getSecureObjectClass ( ) ) ;
}
if ( this . validateConfigAttributes ) {
Iterator iter = this . obtainObjectDefinitionSource ( )
. getConfigAttributeDefinitions ( ) ;
if ( this . validateConfigAttributes ) {
Iterator iter = this . obtainObjectDefinitionSource ( )
. getConfigAttributeDefinitions ( ) ;
if ( iter = = null ) {
if ( logger . isWarnEnabled ( ) ) {
logger . warn (
"Could not validate configuration attributes as the MethodDefinitionSource did not return a ConfigAttributeDefinition Iterator" ) ;
}
} else {
Set set = new HashSet ( ) ;
if ( iter = = null ) {
if ( logger . isWarnEnabled ( ) ) {
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 ( iter . hasNext ( ) ) {
ConfigAttributeDefinition def = ( ConfigAttributeDefinition ) iter
while ( attributes . hasNext ( ) ) {
ConfigAttribute attr = ( ConfigAttribute ) attributes
. 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 ) ;
}
if ( ! this . runAsManager . supports ( attr )
& & ! this . accessDecisionManager . supports ( attr )
& & ( ( this . afterInvocationManager = = null )
| | ! this . afterInvocationManager . supports ( attr ) ) ) {
set . add ( attr ) ;
}
}
}
if ( set . size ( ) = = 0 ) {
if ( logger . isInfoEnabled ( ) ) {
logger . info ( "Validated configuration attributes" ) ;
}
} else {
throw new IllegalArgumentException (
"Unsupported configuration attributes: "
+ set . toString ( ) ) ;
if ( set . size ( ) = = 0 ) {
if ( logger . isInfoEnabled ( ) ) {
logger . info ( "Validated configuration attributes" ) ;
}
} else {
throw new IllegalArgumentException (
"Unsupported configuration attributes: "
+ set . toString ( ) ) ;
}
}
}
}
protected InterceptorStatusToken beforeInvocation ( Object object ) {
Assert . notNull ( object , "Object was null" ) ;
Assert . isTrue ( getSecureObjectClass ( )
. isAssignableFrom ( object . getClass ( ) ) ,
"Security invocation attempted for object "
+ object . getClass ( ) . getName ( )
+ " but AbstractSecurityInterceptor only configured to support secure objects of type: "
+ getSecureObjectClass ( ) ) ;
protected InterceptorStatusToken beforeInvocation ( Object object ) {
Assert . notNull ( object , "Object was null" ) ;
Assert . isTrue ( getSecureObjectClass ( )
. isAssignableFrom ( object . getClass ( ) ) ,
"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 ) & & 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'" ) ;
}
ConfigAttributeDefinition attr = this . obtainObjectDefinitionSource ( )
. getAttributes ( object ) ;
if ( attr ! = null ) {
if ( logger . isDebugEnabled ( ) ) {
logger . debug ( "Secure object: " + object . toString ( )
+ "; ConfigAttributes: " + attr . toString ( ) ) ;
}
if ( ( attr = = null ) & & 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'" ) ;
// We check for just the property we're interested in (we do
// not call Context.validate() like the ContextInterceptor)
if ( SecurityContextHolder . getContext ( ) . getAuthentication ( ) = = null ) {
credentialsNotFound ( messages . getMessage (
"AbstractSecurityInterceptor.authenticationNotFound" ,
"An Authentication object was not found in the SecurityContext" ) ,
object , attr ) ;
}
if ( attr ! = null ) {
if ( logger . isDebugEnabled ( ) ) {
logger . debug ( "Secure object: " + object . toString ( )
+ "; ConfigAttributes: " + attr . toString ( ) ) ;
}
// Attempt authentication if not already authenticated, or user always wants reauthentication
Authentication authenticated ;
// We check for just the property we're interested in (we do
// not call Context.validate() like the ContextInterceptor)
if ( SecurityContextHolder . getContext ( ) . getAuthentication ( ) = = null ) {
credentialsNotFound ( messages . getMessage (
"AbstractSecurityInterceptor.authenticationNotFound" ,
"An Authentication object was not found in the SecurityContext" ) ,
object , attr ) ;
if ( ! SecurityContextHolder . getContext ( ) . getAuthentication ( )
. isAuthenticated ( )
| | alwaysReauthenticate ) {
try {
authenticated = this . authenticationManager . authenticate ( SecurityContextHolder . getContext ( )
. getAuthentication ( ) ) ;
} catch ( AuthenticationException authenticationException ) {
throw authenticationException ;
}
// 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 ( ) ) ;
}
// We don't authenticated.setAuthentication(true), because each provider should do that
if ( logger . isDebugEnabled ( ) ) {
logger . debug ( "Successfully Authenticated: "
+ authenticated . toString ( ) ) ;
}
// Attempt authorization
try {
this . accessDecisionManager . decide ( authenticated , object ,
attr ) ;
} catch ( AccessDeniedException accessDeniedException ) {
AuthorizationFailureEvent event = new AuthorizationFailureEvent ( object ,
attr , authenticated , accessDeniedException ) ;
this . eventPublisher . publishEvent ( event ) ;
throw accessDeniedException ;
}
SecurityContextHolder . getContext ( )
. setAuthentication ( authenticated ) ;
} else {
authenticated = SecurityContextHolder . getContext ( )
. getAuthentication ( ) ;
if ( logger . isDebugEnabled ( ) ) {
logger . debug ( "Authorization successful" ) ;
logger . debug ( "Previously Authenticated: "
+ authenticated . toString ( ) ) ;
}
}
AuthorizedEvent event = new AuthorizedEvent ( object , attr ,
authenticated ) ;
// Attempt authorization
try {
this . accessDecisionManager . decide ( authenticated , object ,
attr ) ;
} catch ( AccessDeniedException accessDeniedException ) {
AuthorizationFailureEvent event = new AuthorizationFailureEvent ( object ,
attr , authenticated , accessDeniedException ) ;
this . eventPublisher . publishEvent ( event ) ;
// Attempt to run as a different user
Authentication runAs = this . runAsManager . buildRunAs ( authenticated ,
object , attr ) ;
throw accessDeniedException ;
}
if ( runAs = = null ) {
if ( logger . isDebugEnabled ( ) ) {
logger . debug (
"RunAsManager did not change Authentication object" ) ;
}
if ( logger . isDebugEnabled ( ) ) {
logger . debug ( "Authorization successful" ) ;
}
return new InterceptorStatusToken ( authenticated , false ,
attr , object ) ; // no further work post-invocation
} else {
if ( logger . isDebugEnabled ( ) ) {
logger . debug ( "Switching to RunAs Authentication: "
+ runAs . toString ( ) ) ;
}
AuthorizedEvent event = new AuthorizedEvent ( object , attr ,
authenticated ) ;
this . eventPublisher . publishEvent ( event ) ;
SecurityContextHolder . getContext ( ) . setAuthentication ( runAs ) ;
// Attempt to run as a different user
Authentication runAs = this . runAsManager . buildRunAs ( authenticated ,
object , attr ) ;
return new InterceptorStatusToken ( authenticated , true ,
attr , object ) ; // revert to token.Authenticated post-invocation
if ( runAs = = null ) {
if ( logger . isDebugEnabled ( ) ) {
logger . debug (
"RunAsManager did not change Authentication object" ) ;
}
return new InterceptorStatusToken ( authenticated , false ,
attr , object ) ; // no further work post-invocation
} else {
if ( logger . isDebugEnabled ( ) ) {
logger . debug ( "Public object - authentication not attempted" ) ;
logger . debug ( "Switching to RunAs Authentication: "
+ runAs . toString ( ) ) ;
}
this . eventPublisher . publishEvent ( new PublicInvocationEvent (
object ) ) ;
SecurityContextHolder . getContext ( ) . setAuthentication ( runAs ) ;
return null ; // no further work post-invocation
return new InterceptorStatusToken ( authenticated , true ,
attr , object ) ; // revert to token.Authenticated post-invocation
}
} else {
if ( logger . isDebugEnabled ( ) ) {
logger . debug ( "Public object - authentication not attempted" ) ;
}
}
/ * *
* Helper method which generates an exception containing the passed
* reason , and publishes an event to the application context .
*
* < p >
* Always throws an exception .
* < / p >
*
* @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 ) ;
this . eventPublisher . publishEvent ( event ) ;
this . eventPublisher . publishEvent ( new PublicInvocationEvent (
object ) ) ;
throw exception ;
return null ; // no further work post-invocation
}
}
public AccessDecisionManager getAccessDecisionManager ( ) {
return accessDecisionManager ;
}
/ * *
* Helper method which generates an exception containing the passed
* reason , and publishes an event to the application context .
*
* < p >
* Always throws an exception .
* < / p >
*
* @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 ) ;
public AfterInvocationManager getAfterInvocationManager ( ) {
return afterInvocationManager ;
}
AuthenticationCredentialsNotFoundEvent event = new AuthenticationCredentialsNotFoundEvent ( secureObject ,
configAttribs , exception ) ;
this . eventPublisher . publishEvent ( event ) ;
public AuthenticationManager getAuthenticationManager ( ) {
return this . authenticationManager ;
}
throw exception ;
}
public RunAsManager getRunAsManager ( ) {
return runAsManager ;
}
public Acces sDecision Manager getAcce ssDecision Manager ( ) {
return accessDecision Manager;
}
/ * *
* 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 boolean isAlwaysReauthenticate ( ) {
return alwaysReauthenticate ;
}
public AfterInvocationManager getAfterInvocationManager ( ) {
return afterInvocationManager ;
}
public boolean isRejectPublicInvocations ( ) {
return rejectPublicInvocations ;
}
public AuthenticationManager getAuthenticationManager ( ) {
return this . authenticationManager ;
}
public boolean isValidateConfigAttributes ( ) {
return validateConfigAttributes ;
}
public RunAsManager getRunAsManager ( ) {
return runAsManager ;
}
public abstract ObjectDefinitionSource obtainObjectDefinitionSource ( ) ;
/ * *
* 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 void setAccessDecisionManager (
AccessDecisionManager accessDecisionManager ) {
this . accessDecisionManager = accessDecisionManager ;
}
public boolean isAlwaysReauthenticate ( ) {
return alwaysReauthenticate ;
}
public void setAfterInvocationManager (
AfterInvocationManager afterInvocationManager ) {
this . afterInvocationManager = afterInvocationManager ;
}
public boolean isRejectPublicInvocations ( ) {
return rejectPublicInvocations ;
}
/ * *
* Indicates whether the < code > AbstractSecurityInterceptor < / code >
* should ignore the { @link Authentication # isAuthenticated ( ) }
* property . Defaults to < code > false < / code > , meaning by default the
* < code > Authentication . isAuthenticated ( ) < / code > property is trusted
* and re - authentication will not occur if the principal has already
* been authenticated .
*
* @param alwaysReauthenticate < code > true < / code > to force
* < code > AbstractSecurityInterceptor < / code > to disregard the
* value of < code > Authentication . isAuthenticated ( ) < / code > and
* always re - authenticate the request ( defaults to
* < code > false < / code > ) .
* /
public void setAlwaysReauthenticate ( boolean alwaysReauthenticate ) {
this . alwaysReauthenticate = alwaysReauthenticate ;
}
public boolean isValidateConfigAttributes ( ) {
return validateConfigAttributes ;
}
public void setApplicationEventPublisher (
ApplicationEventPublisher eventPublisher ) {
this . eventPublisher = eventPublisher ;
}
public abstract ObjectDefinitionSource obtainObjectDefinitionSource ( ) ;
public void setAuthenticationManager ( AuthenticationManager newManager ) {
this . authenticationManager = newManager ;
}
public void setAccessDecisionManager (
AccessDecisionManager accessDecisionManager ) {
this . accessDecisionManager = accessDecisionManager ;
}
public void setMessageSource ( MessageSource messageSource ) {
this . messages = new MessageSourceAccessor ( messageSource ) ;
}
public void setAfterInvocationManager (
AfterInvocationManager afterInvocationManager ) {
this . afterInvocationManager = afterInvocationManager ;
}
/ * *
* By rejecting public invocations ( and setting this property to
* < code > true < / code > ) , essentially you are ensuring that every secure
* object invocation advised by
* < code > AbstractSecurityInterceptor < / code > 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 < code > IllegalArgumentException < / code >
* will be thrown by the < code > AbstractSecurityInterceptor < / code > if
* you set this property to < code > true < / code > and an attempt is made
* to invoke a secure object that has no configuration attributes .
*
* @param rejectPublicInvocations set to < code > true < / code > to reject
* invocations of secure objects that have no configuration
* attributes ( by default it is < code > true < / code > which treats
* undeclared secure objects as "public" or unauthorized )
* /
public void setRejectPublicInvocations ( boolean rejectPublicInvocations ) {
this . rejectPublicInvocations = rejectPublicInvocations ;
}
/ * *
* Indicates whether the < code > AbstractSecurityInterceptor < / code >
* should ignore the { @link Authentication # isAuthenticated ( ) }
* property . Defaults to < code > false < / code > , meaning by default the
* < code > Authentication . isAuthenticated ( ) < / code > property is trusted
* and re - authentication will not occur if the principal has already
* been authenticated .
*
* @param alwaysReauthenticate < code > true < / code > to force
* < code > AbstractSecurityInterceptor < / code > to disregard the
* value of < code > Authentication . isAuthenticated ( ) < / code > and
* always re - authenticate the request ( defaults to
* < code > false < / code > ) .
* /
public void setAlwaysReauthenticate ( boolean alwaysReauthenticate ) {
this . alwaysReauthenticate = alwaysReauthenticate ;
}
public void setRunAsManager ( RunAsManager runAsManager ) {
this . runAsManager = runAsManager ;
}
public void setApplicationEventPublisher (
ApplicationEventPublisher eventPublisher ) {
this . eventPublisher = eventPublisher ;
}
public void setValidateConfigAttributes (
boolean validateConfigAttributes ) {
this . validateConfigAttributes = validateConfigAttributes ;
}
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
* < code > true < / code > ) , essentially you are ensuring that every secure
* object invocation advised by
* < code > AbstractSecurityInterceptor < / code > 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 < code > IllegalArgumentException < / code >
* will be thrown by the < code > AbstractSecurityInterceptor < / code > if
* you set this property to < code > true < / code > and an attempt is made
* to invoke a secure object that has no configuration attributes .
*
* @param rejectPublicInvocations set to < code > true < / code > to reject
* invocations of secure objects that have no configuration
* attributes ( by default it is < code > true < / code > 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 ;
}
}