Reputation: 3723
I use Grails 2.0.0.RC2 and I have an User class like this:
class User {
String username
String password
// Idea from http://grailsrecipes.wordpress.com/2009/04/19/grails-user-registration-and-login/ .
String formPassword
String formPasswordConfirm
// Constraints and validation ommited
static transients = ['formPassword', 'formPasswordConfirm']
def beforeUpdate() {
println("Inside beforeUpdate")
if (formPassword != null)
encodePassword()
}
protected void encodePassword() {
password = formPassword // Just for this case
}
}
When user asks for his password reset, I send him email with a link to reset password page. Reset password form is simple - it contains only two fields: formPassword
and formPasswordConfirm
. I do a simple action in controller: user.formPassword = params["formPassword"]
and user.formPasswordConfirm = params["formPasswordConfirm"]
. Then I do (user.save()
) - and problem begins.
My problem is that beforeUpdate()
is not called. I thought it was a validation problem (it's omiited here), but it's not. As it turns out user.save()
didn't persist user to database! Why? I wanted it to be persisted, password should be changed. But user.isDirty()
is false just before user.save()
. That's because none of persistent properties were changed. That's true. Since user.save()
wasn't called - beforeUpdate()
wasn't called either.
Is this desired behaviour? Maybe it's a Grails bug and beforeUpdate()
should be always called before update and then isDirty()
should be checked? What do you think?
Upvotes: 2
Views: 1671
Reputation: 187529
I don't know if beforeUpdate
should be called when transient fields are updated, but given the choice between
I would bet a lot of money on 2, and very little on 1.
So leaving that aside, I think you've made your domain class a bit more complicated that it needs to be. Specifically, you should be able to achieve your goal with 2 password fields instead of 3.
class User {
static transients = ['passwordConfirm']
def springSecurityService
String password
String passwordConfirm
static constraints = {
password blank: false, validator: {password, self ->
// We only need to check the password confirmation when it is not empty, i.e.
// when a user registers or resets their password
if (self.passwordConfirm) {
password == self.passwordConfirm
}
}
}
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
private void encodePassword() {
password = springSecurityService.encodePassword(password)
passwordConfirm = springSecurityService.encodePassword(passwordConfirm)
}
}
Your reset password controller action should look like this:
def resetPassword = {
User user = User.findByUsername(params.username)
user.password = params.formPassword
user.passwordConfirm = params.formPasswordConfirm
if (user.save()) {
// It worked, send them to the login page or whatever...
} else {
// Validation failed, send them back to the reset password page
}
}
Upvotes: 3
Reputation: 964
This is desired behavior.
The semantics are pretty crystal clear in my opinion, you have not changed any persistent fields, ergo there is nothing to save. The .save() has no way of guessing that in this case some transient property should make you run through the save() (and then beforeUpdate()).
Just add a method that updates the password and you're good to go.
Upvotes: 2