A bit old but might come in hand for anyone else trying groups:
In the User class:
Set<RoleGroup> getAuthorities() {
(UserRoleGroup.findAllByUser(this) as List<UserRoleGroup>)*.roleGroup as Set<RoleGroup>
}
Set<Role> getRoles() {
(UserRoleGroup?.findAllByUser(this)?.roleGroup?.authorities.collect{it}?.flatten() ?: oldRoles) as Set<Role>
}
List<String> getRoleNames() {
(UserRoleGroup?.findAllByUser(this)?.roleGroup?.collect{it.authorities.authority}?.flatten()?: oldRoles.authority) as List<String>
}
//Above will look up from userRoleGroup roleGroup.authorities = UserRole below
Set<Role> getOldRoles() {
(UserRole.findAllByUser(this) as List<Role>)*.role as Set<Role>
}
I had been using Roles even though groups was enabled and authenticating against the old oldRoles method:
import grails.plugin.springsecurity.userdetails.GormUserDetailsService
import grails.transaction.Transactional
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.Authentication
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UsernameNotFoundException
class MySpringSecurityAuthenticator extends GormUserDetailsService{
UserDetails loadUserByUsername(String username, boolean loadRoles)
throws UsernameNotFoundException {
return loadUserByUsername(username)
}
@Transactional
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//enable login with either username or password
User user = User.find {
username == username || attributes.emailAddress == username || userCode == username
}
//if (!user) throw new UsernameNotFoundException('User not found', username)
if (!user) throw new UsernameNotFoundException('User not found')
return loadUserByUsername( user)
}
@Transactional
UserDetails loadUserByUsername(User user) throws UsernameNotFoundException {
if (!user) throw new UsernameNotFoundException('User not found')
//UserDetails.withNewSession {
//getAuthorities(user.oldRoles)
UserDetails userDetails = new org.springframework.security.core.userdetails.User(user.username, user.password,
user.enabled, !user.accountExpired, !user.passwordExpired, !user.accountLocked,getAuthoritiesFromGroup(user.authorities) )
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities())
SecurityContextHolder.getContext().setAuthentication(authentication)
return userDetails
//}
}
public static List<GrantedAuthority> getAuthoritiesFromGroup(Set<RoleGroup> roles) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>()
roles?.each { role ->
println "-- role = ${role} vs ${role.getClass()}"
authorities.add(new SimpleGrantedAuthority(role.name))
}
return authorities
}
public static List<GrantedAuthority> getAuthorities(Set<Role> roles) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>()
roles?.each { role ->
println "-- role = ${role} vs ${role.getClass()}"
authorities.add(new SimpleGrantedAuthority(role.authority))
}
return authorities
}
}
and in my spring/resources.groovy:
userDetailsService(MySpringSecurityAuthenticator){
grailsApplication = ref('grailsApplication')
}
What above had been doing so far was going back to user ROLES and adding them to authentication handler via.. getAuthorities(user.oldRoles)
I have now changed the above method to read in the group names via getAuthoritiesFromGroup(user.authorities)
This simply parses the group names which as a side (effect) of version I am using must also include the word ROLE_GROUPNAME
So now if create
@Transactional
def grantPermission(User user, String role='ROLE_ADMIN', String group='ROLE_SUPERGROUP') {
def adminRole = Role.findByAuthority(role)
if (!adminRole) {
adminRole = new Role(authority: role).save(flush: true)
}
UserRole.create user, adminRole, true
def adminRoleGroup = RoleGroup.findByName(group)
if (!adminRoleGroup) {
adminRoleGroup = new RoleGroup(name: group).save(flush: true)
}
def adminRoleGroupRole = RoleGroupRole.findByRole(adminRole)
if (!adminRoleGroupRole) {
adminRoleGroupRole = new RoleGroupRole(role: adminRole, roleGroup: adminRoleGroup).save(flush: true)
}
def alreadyDone = UserRoleGroup.findByUserAndRoleGroup(user,adminRoleGroup)
if (!alreadyDone) {
new UserRoleGroup(user: user, roleGroup: adminRoleGroup).save(flush: true)
}
}
I am expecting to authenticate against the group names and not the user roles so in short I have had to change my controller to
@Secured("hasAnyRole('ROLE_SUPERGROUP')")
hope it makes sense should be straight forward to follow just takes time to get your head around it all ..
At this point I am still playing around and I wouldn't use as the concrete answer since I am kind of hacking it to say add my groups as my authorities and if I wanted to I could add another hook to go further through each of those groups and each of the actual roles into it as well - unsure what that will produce - at the moment
I want to change it to requestMaps and move it over to db so a lot to change and will decide if i should revert back or use groups I know this way I can configure less names over controllers and rely on the group names..
Either way its under the hood of it all and gives you a clear idea a few years on but may come in handy for others
Update
So I have decided to go with:
,getAuthorities(user.roles)
Where Set<Role> getRoles() { method inside the above bit of code
The reason is quite simple:
results?.each {
it.user=User.get(it.id)
println "-- \n${it.id}:${it.user.roles} \n${it.id}:${it.user.oldRoles}"
7:[Role(authority:ROLE_ADMIN), Role(authority:ROLE_ADMINAHHA)]
7:[Role(authority:ROLE_ADMIN)]
As you can see I added a new user using the getOldRoles I only see 1 role on the getRoles I get 2 roles.. I did add user with 2 roles..
So this will now parse through all the user roles and add them to List<GrantedAuthority> the authentication will still be via the actual ROLE names generated as previously.
It just means when I disable a group from the user this should stop loading that permission for that user..
that is what the model should be doing