go-gorp / gorp

Go Relational Persistence - an ORM-ish library for Go
MIT License
3.74k stars 371 forks source link

No content when executing a hook #317

Open smithwinston opened 8 years ago

smithwinston commented 8 years ago

The way the hooks are designed, I don't believe there's no way to provide any "context" for the hook to use. For example, if I wanted to create PreInsert hook that would encrypt a password field just before insertion into the db, I'd have to use a hard-coded reference to an encryption function or key.

What I really want to be able to do is pass K/V context pairs to the SqlExecutor (implemented by DbMap) so that inside the hook I can retrieve a specific value and use that. In my example, it would be a func reference to call to encrypt the password, or it could be the encryption key etc.

type pw_encryptor func(string) string

...

// Get the password XOR byte from external config
xor := config.GetByte("xorbyte", 0xAB)
encf := func(s string) string {
    b := []byte(s)
    for i := 0; i < len(b); i++ {
        b[i] = b[i] ^ xor
    }
    return string(b)
}

// Or more likely set up a cipher.Block ready to use with AES-256 for proper password encryption!

db.SetContext("pwencrypt", encf)

Later in the hook, I can reach into the SqlExecutor context and retrieve my pwencrypt function and call it knowing it is using context injected in from elsewhere:

func (u *User) PreInsert(s gorp.SqlExecutor) error {
    pwencfn := s.GetContext("pwencrypt").(pw_encryptor)
    u.Password = pwencfn(u.Password)
    return nil
}
avitex commented 8 years ago

@smithwinston For the password handling, create a ignored field on the struct.

type User struct {
    // ...
    Password     string  `db:"-"`
    PasswordSafe string
}

func (u *User) SetPasswordSafe(newPassword string) {
    // bcrypt, pbkdf2 password hasher
    u.PasswordSafe = somehashfunction(newPassword)
}

func (u *User) PreInsert(s gorp.SqlExecutor) error {
    if u.Password != "" {
        u.SetPassword(u.Password)
    }

    return nil
}

This sort of pattern is similar to for example elixir's ecto library, where you would have a "virtual field".

Note: It would recommended to use a one way hashing algorithm for storing passwords. Go's crypto library has both bcrypt and pbkdf2, two widely used algorithms.