Grails spring security, very simple names and roles


So, I’m writing a webapp. It needs to use a swag of user authentication gear in the existing environment, and actually getting started is a bit of a plate of spaghetti, you know? Before you do bit Y, you must first have bit X in place.

So I wanted to create a component that just had some static userids, ignoring passwords, not accessing the database, so that I could create a skeleton for the app.

first – install the grails spring security plugin.
second – run that s2 thingy, but what we are about to do replaces some of it. In particular, the domain classes become irrelevant. However, we still need that login page and some of the other wiring.

Ok. NOw for our custom bits

First, we need to define roles. These will be the strings we put in our <sec: tags.


import org.springframework.security.core.GrantedAuthority

class IbisGrantedAuthority implements GrantedAuthority {
 public static final String NO_ROLES_S = 'NO_ROLES';
 public static final String ADMIN_S = 'ADMIN';
 public static final String EDITOR_S ='EDITOR';
 public static final String MANAGER_S = 'MANAGER';
	
 public static final IbisGrantedAuthority NO_ROLES = new IbisGrantedAuthority(NO_ROLES_S);
 public static final IbisGrantedAuthority ADMIN = new IbisGrantedAuthority(ADMIN_S);
 public static final IbisGrantedAuthority EDITOR = new IbisGrantedAuthority(EDITOR_S);
 public static final IbisGrantedAuthority MANAGER = new IbisGrantedAuthority(MANAGER_S);
	
 public static final Collection NO_ROLES_G = Arrays.asList([NO_ROLES]));
 public static final Collection ADMIN_G =  Arrays.asList([ADMIN, EDITOR, MANAGER]));
 public static final Collection EDITOR_G = Arrays.asList([EDITOR]));
 public static final Collection MANAGER_G = Arrays.asList([MANAGER]));

 private final String auth;

 IbisGrantedAuthority(String auth) { this.auth = auth;}

 @Override String getAuthority() { auth; }
 String toString() { auth; }
}

I’m using strings, but what I could do is make the class itself an enum. Hmm. Might be neater.

Next, the UserDetails class:


import java.security.Principal
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.userdetails.User
class IbisUserDetails extends User implements Principal {
 private static final log = LogFactory.getLog(this)

 public static IbisUserDetails buildUser(String username, Collection auth) {
  return new IbisUserDetails(username, 'password', true, true, true, true, auth);
 }

 public IbisUserDetails(String username,
                        String password,
                        boolean enabled,
                        boolean accountNonExpired,
                        boolean credentialsNonExpired,
                        boolean accountNonLocked,
                        Collection authorities) {
 super(username, password, enabled, accountNonExpired, 
       credentialsNonExpired, accountNonLocked, authorities);
 }

 @Override
 public String getName() { return username; }

 @Override
 public void eraseCredentials () {
  //super.eraseCredentials();
 }
}

Note that erase credentials is overridden. This is because grails wipes the password from the user bean after using it, and as I was using static objects, it created all sorts of problems. Of course, now that I am ignoring the password it doesn’t matter,

And we probably want an authentication object to store the results of out authenticating a user. I’m sure there’s an existing class that does this, but we might want something a bit more complex in future.

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;

class IbisAuthentication implements Authentication {
 UsernamePasswordAuthenticationToken c;
 final IbisUserDetails d;

 IbisAuthentication(UsernamePasswordAuthenticationToken c, IbisUserDetails d) {
  this.c = c;
  this.d = d;
 }

 String getName() { return d.username; }
 Collection getAuthorities() {return d.authorities;}
 Object getCredentials() { return c;}
 Object getDetails() { return "no details, really";}
 Object getPrincipal() { return d;}
 boolean isAuthenticated() { return true; }
 void setAuthenticated(boolean b) {
  throw new UnsupportedOperationException();
 }
}

Fantastic. Now we need a userdetails service and an authentication service. The job of the user details service is to implement “here’s a user id. What’s their details?” This impementation defines three static users – a (admin), e (editor), m (manager), and g (guest).

And oops – I tell a lie, it isn’t using static objects at all. After many, many attempts, this is what I wound up with and works.


import org.springframework.dao.DataAccessException
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.userdetails.UsernameNotFoundException

class IbisUserDetailsService implements UserDetailsService {
 @Override
 IbisUserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
  if('a' == username) {
    return IbisUserDetails.buildUser(username, 
                                     IbisGrantedAuthority.ADMIN_G);
  }
  if('e' == username) {
   return IbisUserDetails.buildUser(username, 
                                    IbisGrantedAuthority.EDITOR_G);
  }
  if('m' == username) {
   return IbisUserDetails.buildUser(username, 
                                    IbisGrantedAuthority.MANAGER_G);
  }
  if('g' == username) {
   return IbisUserDetails.buildUser(username, 
                                    IbisGrantedAuthority.NO_ROLES_G);
  }
  throw new UsernameNotFoundException(username);
 }
}

And finally, the Authentication service, the bit that has the job “Here’s some credentials, can you make sense of them?”


class IbisAuthenticationService implements AuthenticationProvider {
 @Autowired
 IbisUserDetailsService ibisUserDetailsService;

 @Override
 boolean supports(Class aClass) {
  if(UsernamePasswordAuthenticationToken.class.isAssignableFrom(aClass)) {
   return true;
  }
  return false;
 }

 @Override
 Authentication authenticate(Authentication authentication) throws AuthenticationException {
  if(authentication instanceof UsernamePasswordAuthenticationToken) {
   return authenticate((UsernamePasswordAuthenticationToken) authentication);
  }
  // else if(other token type) { handle that token type }
  else {
    // this should never happen
    throw new IllegalArgumentException("Unrecognised authentication type - this never happens");
  }
 }

 Authentication authenticate(UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
  String userName = (String) authentication.principal;
  String password = (String) authentication.credentials;

  // this code completely ignores password
  try {
   IbisUserDetails d = ibisUserDetailsService.loadUserByUsername(userName);
   return new IbisAuthentication(authentication, d);
  }
  catch(UsernameNotFoundException ex) {
   throw new BadCredentialsException(userName, ex);
  }
 }
}

The unrecognised type never happens because it is spring’s job to check what kinds of authentication I take before sending them to me.

OK! NOw to wire it up!

First, spring beans in conf/spring/resources.groovy:


beans = {
 userDetailsService(au.gov.environment.ibis.grails.ibisauth.IbisUserDetailsService);
 ibisAuthenticationService(au.gov.environment.ibis.grails.ibisauth.IbisAuthenticationService);
}

Defining the user details bean is key, as this name is used by other grails authenticators.

Next, we wire in our authenticator in Config.groovy


grails.plugins.springsecurity.providerNames = [
 'ibisAuthenticationService',
 // 'anonymousAuthenticationProvider',
 'rememberMeAuthenticationProvider'
]

The rememberMe provider is provided by grails, and this provider (I expect) uses userDetailsService, which is why it has to be wired in using that name.

And, that’s about it!

I have a manager page. The code for my main menu bar in the main layout looks like this:


<div style="display: inline; float:left; ">
  <g:link controller='workbench'>Workbench</g:link>
</div>
<sec:ifAnyGranted roles="MANAGER">
  <div style="display: inline;  float:left; ">
    <g:link controller='management'>Manage</g:link>
  </div>
</sec:ifAnyGranted>

And the management item only comes on if the user logs on as ‘a’ or ‘m’. Furthermore, the controller itself is protected with an annotation:


import grails.plugins.springsecurity.Secured

@Secured(["hasRole('MANAGER')"])
@Mixin(ErrorblockMixin)
class ManagementController {

If a user is not logged in and goes to the management URL, spring security cuts in and sends them to the login page.

Took a while, and there’s a lot there, but this is a rock-bottom implmentation of user access control in grails. NOw I can write the rest of the damn app, and wire in our you-beaut single-signon authentication gear later.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: