Monday, January 12, 2009

Spring Acegi: Force Re-Authenticating Other Users When Roles Change

Here's the Scenario that I have an issue with:

A System Administrator edits a user role and the user whose role is currently logged in could possibly already be logged in.

When System Administrators log into a website and edit a user role, the default implementation of Spring Acegi does not re-authenticate the user if the user is currently logged in to the system. In short, if the user in question is an administrator and you remove the admin role from the user the changes doesn't take effect until the next time this user re-authenticates--i.e. log-out then log back in.

The Solution:

The ideal solution would be for the system without turning on the alwaysReauthenticate to detect that the user information has changed and will automatically re-authenticate user in question is already logged in to the system.

How do I get acegi to re-authenticate the user who is currently logged in whenever this person's user roles are edited? I don't want to turn on alwaysReauthenticate property on the FilterSecurityInterceptor. The solution I came up with is whenever the EditUserFormController executes (only executed by admins) to save the user information (including but not limited to user roles), I clear the userCache entry for that user and added a filter that will check whether the user exists in the userCache. If it doesn't it will set the Authentication.authenticated property to false, hence to be forced to re-authenticate in FilterSecurityInterceptor. This filter executes before the FilterSecurityInterceptor in the filter chain.

This seems to work fine but without turning on the alwaysReauthenticate property this is the most perfomance-saavy solution until a functionality like this (if it makes sense to implement) is implemented in Spring Acegi.

Here's the code snippet from the filter I created:
protected void reAuthenticateAsNeeded() {
   Authentication authentication = SecurityContextHolder.getContext()
   if (!("anonymous".equalsIgnoreCase(authentication.getName()))
           && authentication.isAuthenticated()
           && m_userCache.getUserFromCache(authentication.getName()) == null) {

Please note that the following FilterSecurityInterceptor is then executed and will re-authenticate the user. (see also AbstractSecurityInterceptor#beforeInvocation())

No comments: