18 July 2005

Testing Acegi Security's Provider-based Authentication

I am taking down this note as this process is very hard to remember when finalized and has been stabilized. The authentication process is very straightforward because most of the dirty works are handled by authentication providers. We will take the assumption that the reader is already familiar with Spring's application-context XML configurations in Acegi. We'll write a testable code based on JUnit. But first we have to enumerate the steps our authentication method will going to take:


  1. Initialize a ListableBeanFactory instance

  2. Initialize the net.sf.acegisecurity.Authentication interface with a proper reference implementation

  3. Obtain an instance of the from the ListableBeanFactory configuration

  4. Invoke the AuthenticationProvider.authenticate()
  5. Get the instance of the net.sf.acegisecurity.providers.dao.User from the AuthenticationProvider.authenticate().getPrincipal()
  6. Check ALL of the following from the User object: isEnabled(), isAccountNonLocked(), isAccountNonExpired(), isCredentialsNonExpired()
  7. Register the Authentication object to the SecureContext

  8. Register the SecureContext object to ContextHolder.setContext() static method.


In code, this is how it looks like:

Listing 1:


public boolean authenticate(String username, String password) {

Authentication authentication =
new UsernamePasswordAuthenticationToken(username, password);

SecureContext secureContext = new SecureContextImpl();

//have chose daoAuthenticationProvider for this.
AuthenticationProvider provider =
(AuthenticationProvider)beanFactory.getBean("daoAuthenticationProvider");
Authentication auth = provider.authenticate(authentication);
System.out.println(auth.getName());
User user = (User)auth.getPrincipal();

if (user.isAccountNonExpired() && user.isAccountNonLocked() &&
user.isCredentialsNonExpired() && user.isEnabled()){
auth.setAuthenticated(true);
}

secureContext.setAuthentication(auth);
ContextHolder.setContext(secureContext);
return auth.isAuthenticated();

}



The test code should assert for the isAuthenticated() method with something like this:

Listing 1:


public void testAuthenticate() {

String[] contexts = {"applicationContext-acegi-security.xml",
"applicationContext-common-business.xml",
"applicationContext-common-authorization.xml"};

ListableBeanFactory beanFactory =
new ClassPathXmlApplicationContext(contexts);

UserAuthenticate authenticate = new UserAuthenticate(beanFactory);

//we assume that you are familiar with JUnit
assertTrue(authenticate.authenticate("scott", "wombat"));

}



To do this test properly, all the AOP-based interceptors must be disabled since it will not be ran on a web container or application server. From a controller class (such as Webwork2 action classes) the authenticate() method at Listing 2 will be shorter because the ContextHolder already has the security credentials provided by AOP interceptors executed during initialization of UsernamePasswordAuthenticationToken class. But for the sake of making a testable code, I have provided those checks for clarity.

No comments: