Search code examples
grailsspring-securityspring-security-rest

Grails 3.1.7 Spring Security Issue when create new User by GORM Scaffolding


I am doing my firsts steps in Grails (version 3.1.7), and I am doing an application which needs user authentication. This app is a web application that also provide some REST functionality, so I need a web login and a "rest" login with token.

I am using spring-security-core:3.1.0 and spring-security-rest:2.0.0.M2 for these propose and both logins are working properly.

Now I am having some troubles when I try to create a new user via the CRUD generated with grails generate-all package.User, I generated the view properly (I have a client class that the user could have or not so if the user has it I provide the client fields in the same create section too because a client can have only one user). When I save it I got a internal server error 500:

URI /web/webUser/save, Class java.lang.NullPointerException, Message null

in the file: \web\WebUserController.groovy and the line is: webUser.save flush:true

the trace is:

Line | Method
80 | doFilter  in grails.plugin.springsecurity.rest.RestLogoutFilter
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|     64 | doFilter  in grails.plugin.springsecurity.web.UpdateRequestContextHolderExceptionTranslationFilter
|     53 | doFilter  in grails.plugin.springsecurity.web.filter.GrailsAnonymousAuthenticationFilter
|    143 | doFilter  in grails.plugin.springsecurity.rest.RestAuthenticationFilter
|     62 | doFilter  in grails.plugin.springsecurity.web.authentication.logout.MutableLogoutFilter
|     58 | doFilter  in grails.plugin.springsecurity.web.SecurityRequestHolderFilter
|   1145 | runWorker in java.util.concurrent.ThreadPoolExecutor
|    615 | run       in java.util.concurrent.ThreadPoolExecutor$Worker
^    745 | run . . . in java.lang.Thread

Caused by NullPointerException: null
->>   47 | $tt__save in WebUserController.groovy
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|     96 | doInTransaction in grails.transaction.GrailsTransactionTemplate$2
|     93 | execute . in grails.transaction.GrailsTransactionTemplate
|     96 | doInTransaction in grails.transaction.GrailsTransactionTemplate$2
|     93 | execute . in grails.transaction.GrailsTransactionTemplate
|     80 | doFilter  in grails.plugin.springsecurity.rest.RestLogoutFilter
|     64 | doFilter  in  grails.plugin.springsecurity.web.UpdateRequestContextHolderExceptionTranslationFilter
|     53 | doFilter  in grails.plugin.springsecurity.web.filter.GrailsAnonymousAuthenticationFilter
|    143 | doFilter  in grails.plugin.springsecurity.rest.RestAuthenticationFilter
|     62 | doFilter  in grails.plugin.springsecurity.web.authentication.logout.MutableLogoutFilter
|     58 | doFilter  in grails.plugin.springsecurity.web.SecurityRequestHolderFilter
|   1145 | runWorker in java.util.concurrent.ThreadPoolExecutor
|    615 | run . . . in java.util.concurrent.ThreadPoolExecutor$Worker
^    745 | run       in java.lang.Thread

I am not sure if I have something wrong in the security plugin configuration or in the user and client domain class, or in the userController.

Edit: Updete info!

@Transactional(readOnly = true)

class WebUserController {

static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
static namespace = 'web'

def springSecurityService
@Transactional
def save(WebUser webUser) {
    if (webUser == null) {
        transactionStatus.setRollbackOnly()
        notFound()
        return
    }

    if (webUser.hasErrors()) {
        transactionStatus.setRollbackOnly()
        respond webUser.errors, view:'create'
        return
    }

    webUser.save flush:true // line where the NullPointerException is threw 

    if (webUser.isAdmin){
        UserRole.create webUser, Role.findByAuthority('ROLE_ADMIN')
    } else {
        UserRole.create webUser, Role.findByAuthority('ROLE_CLIENT')
    }

    request.withFormat {
        form multipartForm {
            flash.message = message(code: 'default.created.message', args: [message(code: 'webUser.label', default: 'WebUser'), webUser.id])
            redirect webUser
        }
        '*' { respond webUser, [status: CREATED] }
    }
}

And the code in the domin:

transient springSecurityService

String username
String password
boolean enabled = true
boolean accountExpired
boolean accountLocked
boolean passwordExpired

static transients = ['isAdmin', 'springSecurityService']
boolean isAdmin
Client client
String name
String email
Date lastVisit
static hasMany = [orders: BOrder]

static constraints = {
    username size: 5..15, blank: false, unique: true
    password size: 5..15, blank: false, password: true
    email email: true, blank: false, unique: true
    name size: 0..50, nullable: true
    lastVisit nullable: true
    client nullable: true
}

The error happens when the client is either null or not. And debugging if I add a afterInsert in the WebUser domain class, I can see the Client id generated if it is not null, but the WebUser id is not generated, and I do not see any error message at that point.


Solution

  • Well I found where was the issue.

    The problem was that I added a constraint to the password size, and the validate were executed before the password was hashed, so the validate pass, then the password was hashed and it doesn't have less than the 15 characters, so it failed when it try to persist the user in the database and then it threw the null pointer exception.

    Removing or changing the max length restriction to the password, the problem were solved.

    The only weird thing to me is the error message that I got, which didn't tell nothing relevant in my opinion.