isModified and pre-save mongoose...Nodejs
Asked Answered
G

4

9

Hi i want save with hashed password only if password is change, so i used isModified function in pre-save, but its always return false even i changed the password. The reason that i am trying to do this is because i dont want to change and save my password when i change other properties.

router.post('/changepw', isAuthenticated, function (req, res, next) {
    User.findOneAndUpdate({_id: req.user._id}, {$set: req.body},{ new: true }, function (err, user){

        if (err) {
          return err;
        } 
        else {

          if (req.body.password) {
            user.password = req.body.password;
            user.save();
          } else { 

          }

        }
        res.redirect('/profile');
    });
});

like here i dont want to change my password when i change my graduated value.

router.post('/edit', isAuthenticated, function (req, res, next) {
    User.findOneAndUpdate({
        _id: req.user._id
    }, {
        $set: {
            name: req.body.name,
            phone: req.body.phone,
            classc: req.body.classc,
            major: req.body.major,
            minor: req.body.minor,
            linkedin: req.body.linkedin,
            bio: req.body.bio
        }
    }, {
        new: true
    }, function (err, user, done) {

        if (err) {
            return err;
        } else {

            if (typeof req.body.graduated == 'undefined') {
                user.graduated = false;


            } else if (typeof req.body.graduated == 'string') {

                user.graduated = true;

            }

            user.save();
        }
        res.redirect('/profile');
    });
});
userSchema.pre('save', function(next) {
console.log(this.isModified('password'));                                                                                                                                        
    if(this.password && this.isModified('password')){                                                                                                                                                                                                                                                                                      
        this.password  = bcrypt.hashSync(this.password, bcrypt.genSaltSync(8),null);                                                                                                             
    }

    next()                                                                                                                                                                     
}); 

any suggestions?

Greyson answered 29/5, 2018 at 10:15 Comment(0)
D
10

note that when you're using findAndUpdate() method, the pre-save hook is not triggered. Check the Mongoose documentation by using new hooks: http://mongoosejs.com/docs/middleware.html#notes.

Danieledaniell answered 29/5, 2018 at 13:38 Comment(0)
M
10

Try this

userSchema.pre('save', async function (next) {
  // Only run this function if password was moddified (not on other update functions)
  if (!this.isModified('password')) return next();
  // Hash password with strength of 12
  this.password = await bcrypt.hash(this.password, 12);
  //remove the confirm field 
  this.passwordConfirm = undefined;
});
Marchelle answered 26/7, 2020 at 18:49 Comment(3)
This is not going to be called during User.findOneAndUpdate.Bibliolatry
As per the docs, it does mongoosejs.com/docs/middleware.html#preMarchelle
Definitely not. Further down the page you linked to there is a chapter "Notes on findAndUpdate() and Query Middleware". It says "Pre and post save() hooks are not executed on update(), findOneAndUpdate(), etc." Also, "You cannot access the document being updated in pre('updateOne') or pre('findOneAndUpdate') query middleware." The OP modifies user twice. Maybe a combination of findById() and user.save()would be better. I will add an answer with a suggestion.Bibliolatry
B
4

findOneAndUpdate()already updates user, so in the callback function you provided user is already up to date. When you call user.save() in that callback, the pre save() hook will be called, but isModified('password') will be false.

If you provide a new password with req.body, the password will land in the database unhashed, since "Pre and post save() hooks are not executed on update(), findOneAndUpdate(), etc." (see documentation). If you then compare the password in req.body and user, they will be the same and you cannot decide whether to trigger the hash function with a save() or not.

Leave your pre save() hook as is and do the update as a combination of findById() and save():

  User.findById(req.user._id, (err, user) => {
    if (err)
      handleYourErrorAndLeave(err)
  
    // Update all user attributes which are different or missing from user with values from req.body
    Object.assign(user, req.body)

    // Save the updated user object.
    // pre save() hook will be triggered and isModified('password') should be correct
    user.save()
      .then(savedUser => {
        res.redirect('/profile')
      }) 
  })
Bibliolatry answered 22/10, 2021 at 11:34 Comment(0)
L
0

It's better to manupulate password from model. This is the one place where you will be able to do update or skeep update a password along with other values based on your needs.

// Encrypt password before saving into database
userSchema.pre("save", async 
 function(next) {
 if (!this.isModified("password")) {
  return next();
 }
 const salt = await bcrypt.genSalt(10);
 const hashedPassword = await bcrypt.hash(this.password, salt);
 this.password = hashedPassword;
 next();
});
Lamellirostral answered 10/3 at 11:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.