Search code examples
mysqlgogo-gorm

gorm hash password with BeforeUpdate does not update the password with hashed value


I have a very simple User model, and my entire main.go is the following

package main

import (
  "fmt"

  "github.com/jinzhu/gorm"
  _ "github.com/jinzhu/gorm/dialects/mysql"
  "golang.org/x/crypto/bcrypt"
)

// User : the user data model
type User struct {
  gorm.Model

  Username string `gorm:"type:varchar(40);unique" json:"username,omitempty"`
  Password string `gorm:"size:255" json:"password,omitempty"`
}

// BeforeSave : hook before a user is saved
func (u *User) BeforeSave(scope *gorm.Scope) (err error) {
  fmt.Println("before save")
  fmt.Println(u.Password)
  if u.Password != "" {
     hash, err := MakePassword(u.Password)
     if err != nil {
        return nil
     }
     u.Password = hash
  }

  fmt.Println(u.Password)
  return
}

// MakePassword : Encrypt user password
func MakePassword(password string) (string, error) {
  bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
  return string(bytes), err
}

func main() {
  var connectionString = fmt.Sprintf(
    "%s:%s@/%s?charset=utf8&parseTime=True&loc=Local",
    "root", "password", "project",
  )

  db, _ := gorm.Open("mysql", connectionString)
  db.AutoMigrate(&User{})
  db.Save(&User{
    Username: "name1",
    Password: "123",
  })

  db.Model(&User{
    Model: gorm.Model{ID: 1},
  }).Update(&User{
    Username: "name2",
    Password: "12345",
  })
}

For the first time of my creation, the password is created and save as a hash string (I commented out the second updating part and tested it), but when I updating it, it is saved as plain text.

From the console, I can see the Println message, BeforeUpdate is trigged, and I see the plain password and MD5 of it, but in database, it is saved as plain text.

before save
123
$2a$10$Vknv/uu7tAPRQSddPVlQ7OodIHZJmRPKktjb98U8U5.GT/OLQeQE2
before save
12345
$2a$10$K0ZkLH7slfiFmkOe5DTKr.DGNvR6HtpjxCS/1svf2ZEvfTXVkMkvu

Any ideas on how I can fix it ?


Solution

  • According to the docs in order to change the values being updated you must set them through the gorm Scope param. The User struct in this case it's not meant to be used for modifications. You should use SetColumn instead.

    // BeforeUpdate : hook before a user is updated
    func (u *User) BeforeUpdate(scope *gorm.Scope) (err error) {
        fmt.Println("before update")
        fmt.Println(u.Password)
    
        if u.Password != "" {
            hash, err := MakePassword(u.Password)
            if err != nil {
                return err
            }
            scope.SetColumn("Password", hash)
        }
    
        fmt.Println(u.Password)
        return
    }