Lately I have been working on Spring security with this configuration:
- with groups
- database stored in tenant database
First problem I detected was that the PerTenantUserDetailsService implemented in https://johnny.prpr.no/spring-security-multitenant/ was annotated with @CurrentTenant, which is wrong.
Furthermore I wanted to add fullName to the UserDetails, for easy lookup. This was achieved by following the plugin documentation.
Then updating the UserDetailsService.
@Slf4j
class PrprTenantUserDetailsService implements GrailsUserDetailsService {
public static final String TENANT_ID = 'tenantId'
UserService userService
@Override
UserDetails loadUserByUsername(String username, boolean loadRoles) throws UsernameNotFoundException, DataAccessException {
loadUserByUsername(username)
}
@Transactional(readOnly = true, noRollbackFor = [IllegalArgumentException, UsernameNotFoundException])
@Override
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.debug("loadUserByUsername(${username})")
UserDetails result = null
manuallyWireServices()
String tenantName = UserToTenant.getTenant(username)
Tenants.withId(tenantName) {
MDC.put(TENANT_ID,tenantName)
User user = userService.findByUsername(username)
if (!user) throw new NoStackUsernameNotFoundException()
if (user) {
result = new PrprUserDetails(user.username,
user.password,
user.enabled,
!user.accountExpired,
!user.passwordExpired,
!user.accountLocked,
securityService.getRoles(username),
user.id,
user.name)
}
MDC.put(TENANT_ID, '')
}
log.debug("loadUserByUsername($username) == $result")
result
}
private void manuallyWireServices() {
if (userService) {
return
}
GrailsApplication grailsApplication = Holders.grailsApplication
userService = (UserService) grailsApplication.mainContext.getBean('userService')
}
}
Beware that the authorities parameter sent to UserDetails is all the roles, so when using groups you will have to collect roles from the users group memberships.