2011-12-01 5 views
2

J'utilise Grails 2.0.0.RC2 et j'ai une classe d'utilisateur comme ceci:AvantMAJ pas appelé quand seulement transitoires a changé

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 
    } 
} 

Lorsque l'utilisateur demande son nouveau mot de passe, je l'envoie par courriel avec un lien pour réinitialiser la page de mot de passe. Le formulaire de réinitialisation du mot de passe est simple. Il ne contient que deux champs: formPassword et formPasswordConfirm. Je fais une action simple dans le contrôleur: user.formPassword = params["formPassword"] et user.formPasswordConfirm = params["formPasswordConfirm"]. Alors je fais (user.save()) - et le problème commence.

Mon problème est que beforeUpdate() n'est pas appelé. Je pensais que c'était un problème de validation (c'est omis ici), mais ce n'est pas le cas. Comme il s'avère que user.save() n'a pas persisté utilisateur à la base de données! Pourquoi? Je voulais qu'il soit persistant, le mot de passe devrait être changé. Mais user.isDirty() est faux juste avant user.save(). En effet, aucune propriété persistante n'a été modifiée. C'est vrai. Depuis user.save() n'a pas été appelé - beforeUpdate() n'a pas été appelé non plus.

Est-ce le comportement désiré? Peut-être que c'est un bug de Grails et beforeUpdate() devrait toujours être appelé avant la mise à jour, puis isDirty() devrait être vérifié? Qu'est-ce que tu penses?

Répondre

3

Je ne sais pas si beforeUpdate doit être appelé lorsque les champs transitoires sont mis à jour, mais étant donné le choix entre

  1. c'est un Grails/Hibernate bug
  2. c'est le comportement attendu car aucune mise à jour DB serait se produisent lorsque les champs transitoires sont modifiés

Je serais prêt à parier beaucoup d'argent sur 2, et très peu sur 1.

donc En laissant cela de côté, je pense que vous avez rendu votre classe de domaine un peu plus compliquée qu'elle doit l'être. Plus précisément, vous devriez être en mesure d'atteindre votre objectif avec 2 champs de mot de passe au lieu de 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) 
    } 

} 

Votre action du contrôleur de mot de passe de réinitialisation doit ressembler à ceci:

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  
    } 
} 
+0

Merci pour votre réponse. J'aime votre solution avec 2 champs de mot de passe. Il résout que beforeUpdate ne soit pas appelé aussi. Merci. –

+0

De rien. C'est presque identique à la solution dans ma propre application (qui utilise également la sécurité de printemps), et cela fonctionne bien –

+0

mais j'ai encore besoin de résoudre un autre problème. Après la phase de validation, comment ecnode-t-on le mot de passe stocké sur db? Dans Grails 2.0.0.RC2, beforeUpdate se produit avant la validation. –

1

Ceci est le comportement souhaité.

La sémantique est assez claire à mon avis, vous n'avez pas changé de champs persistants, il n'y a rien à sauvegarder. Le .save() n'a aucun moyen de deviner que dans ce cas, une propriété transitoire devrait vous faire courir à travers le save() (et ensuite beforeUpdate()).

Juste ajouter une méthode qui met à jour le mot de passe et vous êtes prêt à partir.

+0

Nous vous remercions de votre réponse. Ce n'est pas si facile avec une méthode qui met à jour le mot de passe. Je compte sur une validation de mot de passe lors de la phase de validation, c'est pourquoi encodePassword() est appelée dans beforeUpdate(). –

Questions connexes