1
votes

Background

I'm using the Spring Security Grails plugin. Because my User and Role classes are not GORM objects, I I've replaced the UserDetailsService provided by the plugin with my own implementation:

class CustomUserDetailsService implements UserDetailsService {

    static transactional = false
    private static final log = LogFactory.getLog(this)

    @Autowired
    private UserManager userManager

    @Autowired
    private RoleManager roleManager

    UserDetails loadUserByUsername(String username) {
        User user = userManager.getUserByEmail(username)
        UserDetails userDetails = new UserAdapter(user, roleManager)

        log.debug "user '$username' has roles: ${userDetails.authorities?.authority}"
        userDetails       
    }
}

Problem

When I login, I see the following message is logged from CustomUserDetailsService.loadUserByUsername()

user '[email protected]' has roles: [USER]

So it seems that the user has been assigned the USER role. However, when I then try and access an action of this controller:

@Secured(['ROLE_USER', 'ROLE_ADMINISTRATOR'])
class MyProfileController {
    def someAction = { // impl omitted }
}

I get bounced to the access denied page. I'm pretty sure that the user is logged in, because the access denied page contains markup such as

<sec:ifLoggedIn>
  protected content
</sec:ifLoggedIn>

and the protected content is displayed. So it seems that somehow the USER role is not associated with the current user, when the controller authorisation is performed. The log message suggests that the UserDetailsService is OK.

1
Is USER in the println the same as ROLE_USER?tim_yates
getCurrentUser() is a utility method for users, but it's not used by the plugin or Spring Security, so it's not related to this issue. Try setting the 'org.springframework.security' category to debug in your Log4j config - Spring Security is good about dumping verbose info about auth failures.Burt Beckwith
Ah, just saw Tim's question - that's probably it. Roles have to start with 'ROLE_' otherwise the RoleVoter ignores them.Burt Beckwith
@BurtBeckwith @tim_yates Are you saying that GrantedAuthority.authority should return a String that begins with 'ROLE_'?Dónal
Yes, always - roles need a prefix to differentiate them from non-role tokens like IS_AUTHENTICATED_FULLY and it's currently not configurable to anything other than "ROLE_". You can convert them to more presentable strings in your UI of course.Burt Beckwith

1 Answers

0
votes

Solution

The solution is to make sure that the role names in the domain class/database begin with "ROLE_", as per the annotation parameters.

Acknowledgements

All credit for this answer goes to @BurtBeckwith and @tim_yates, who provided the solution in comments. I'm converting their comments to an answer, as future readers may easily miss their comments.