Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I run model validations before the setterMethod in Sequelize?

I want to save a hashed password. I'm using a setterMethod for this:

module.exports = (sequelize, DataTypes) ->
  sequelize.define 'User',
    # other model fields [...]
    password:
      type: DataTypes.STRING
      validate: notEmpty: msg: 'You need to set a password.'
      set: (pw) ->
        salt = bcrypt.genSaltSync(10)
        hash = bcrypt.hashSync(pw, salt)
        @setDataValue('password', hash)

The setter runs first. An empty string password ('') is hashed into a non-empty one (say $2a$10$pDDIGnV.r47i9YOv0Fls/euQ0yYvfyq8T1SyP9VRQsTUAqptNmxXO).

When the validator validates, the password is not empty anymore.

How can I validate the password before the setter?

I looked into hooks but they don't mention setters either.

I'm using [email protected].

like image 367
dschwertfeger Avatar asked Aug 23 '15 11:08

dschwertfeger


People also ask

How do you handle a validation error in Sequelize?

Just change your function to specifically use the Promise -> next() pattern. Of course the easiest way to handle unique constraints is by setting unique: true on the field definition itself: var User = sequelize. define('User', { username: { type: DataTypes.

What is Sequelize constraint?

Adding constraints between tables means that tables must be created in the database in a certain order, when using sequelize. sync . If Task has a reference to User , the User table must be created before the Task table can be created.


1 Answers

I solved this problem by using two fields, one being type VIRTUAL that handles the input and validation, and one being type STRING that holds the hashed password.

This example is not coffeescript but you should be able to translate easily.

password_hash: {
  type: DatabaseTypes.STRING,
  allowNull: false,
  validate: {
    notEmpty: true,
  },
},
password: {
  type: DatabaseTypes.VIRTUAL,
  allowNull: false,
  // note that arrow functions cannot access "this", so use the form:
  set: function setPassword(val) {
    // trigger validation on "password" field
    this.setDataValue('password', val);

    // hash the password, this can be done in one step by passing the
    // number of salt rounds instead of the salt string.
    this.setDataValue('password_hash', bcrypt.hashSync(val, 10));
  },
  validate: {
    notEmpty: {
       message: 'You need to set a password.',
    },
  },
},

When you authenticate the user compare the entered password to User.password_hash rather than User.password.

instanceMethods: {
  // authenticate user given a password
  authenticate(password) {
    return bcrypt.compareSync(password, this.password_hash);
  },
},

You can then call this instance method to authenticate a User.

User.findById(userId)
.then((user) => {
  if (user.authenticate(password)) {
    console.log('Authenticated');
  } else {
    console.log('Not authenticated');
  }
});
like image 192
doublesharp Avatar answered Oct 02 '22 14:10

doublesharp