bcryptjs version: "bcryptjs": "^2.4.3"
mongoose version: "mongoose": "^6.0.12"
I am trying to encrypt user password on user creation or password update. When I create a single user with User.create() everything works as intended (password is encrypted). When I use User.insertMany() the data is inserted successfully but the password is not encrypted. This is my schema:
const userSchema = mongoose.Schema(
{
name: {
type: String,
required: true
},
surname: {
type: String,
required: true
},
voterId: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true,
unique: true,
},
votedFor: [
{
type: mongoose.Schema.ObjectId,
ref: 'Election'
}
],
finishedVoting: {
type: Boolean,
required: true,
default: false
},
isAdmin: {
type: Boolean,
required: true,
default: false,
},
},
{
timestamps: true,
}
)
userSchema.pre('save', async function(next) {
// Only run this function if password was actually modified
if (!this.isModified('password')) return next();
// Hash the password with salt 10
this.password = await bcrypt.hash(this.password, 10);
next();
});
This is some sample data that I am trying to insert:
const voters = [
{
name: "Sherali",
surname: "Samandarov",
voterId: "194199",
password: "FA654644", //will be encrypted
isAdmin: false,
finishedVoting: false
// votedFor: [Object], //
},
{
name: "Sherali",
surname: "Samandarov",
voterId: "184183",
password: "MB454644", //will be encrypted
isAdmin: false,
finishedVoting: false
// votedFor: [Object], //
},
{
name: "Sherali",
surname: "Samandarov",
voterId: "194324",
password: "FA651684", //will be encrypted
isAdmin: false,
finishedVoting: false
// votedFor: [Object], //
}
]
I am guessing that userSchema.pre('save', ...) does not trigger on insertMany() for some reason
Solved.
I was able to solve the problem by following #victorkt's answer:
Use pre('validate') instead of pre('save') to set the value for the
required field. Mongoose validates documents before saving, therefore
your save middleware won't be called if there are validation errors.
Switching the middleware from save to validate will make your function
set the password field before it is validated.
So,
userSchema.pre('validate', async function(next) {
// Only run this function if password was actually modified
if (!this.isModified('password')) return next();
// Hash the password with salt 10
this.password = await bcrypt.hash(this.password, 10);
next();
});
Related
i have this model with username and email set uniqe to false
const UserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
minlength: 3,
maxlength: 20,
unique: false,
},
email: {
type: String,
required: true,
minlength: 5,
maxlength: 64,
lowercase: true,
unique: false,
},
mailboxLink: {
type: String,
required: true,
unique: true,
default: nanoid(),
},
createdat: { type: String, default: dateJakarta },
});
and 1 user in my mongodb database
{"_id":{"$oid":"622eec9de7f66d1d633061e7"},"username":"jhon","email":"email#gmail.com","mailboxLink":"mfdYTDK","createdat":"2022-03-14 14:19:01","__v":0}
but when i'm trying to register the same username and email
userRouter.post("/register", async (request, response) => {
const newUser = new userSchema(request.body); // body: {username, email}
await newUser.save((err, user) => {
if (err) {
console.log(err);
} else {
const token = newUser.generateAuthToken();
response.status(201).json({
token,
user,
message: "Register successfully",
});
}
});
});
i got this error from console.log(err);
MongoServerError: E11000 duplicate key error collection: PigeonProjekt.users index: email_1 dup key: { email: "email#gmail.com" }
index: 0,
code: 11000,
keyPattern: { email: 1 },
keyValue: { email: 'email#gmail.com' }
could someone help me solve this problem.
If you can, try dropping the database or the collection itself then test again. If you first set the email field to unique: true and then inserted the user that you have in the database, but later changed the unique value of email to false in your schema, this will not update the collection itself. In short, rules in the database for email unique: true still apply and you need to remove them.
I am new to mongoose and I have searched alot about it and was not able to find out the ans. Please help, thanks in advance.
const user = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true
},
rollno: {
type: Number,
required: true,
unique: true,
},
password : {
type: String,
required: true,
},
isVoter: {
type: Boolean,
required: true,
default: true
}
}, {
timestamps: true
});
Can I have a schema which would be dependent on the value of isVoter. For example if value of isVoter is false then we should have schema like this :
const user = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true
},
rollno: {
type: Number,
required: true,
unique: true,
},
password : {
type: String,
required: true,
},
isVoter: {
type: Boolean,
required: true,
default: true
},
promises: [
{
type: String
}
]
, {
timestamps: true
});
You can defined if one variable is required or not based in another property in this way:
promises: [
{
type: String,
required: function(){
return this.isVoter == false
}
}
]
So, promises will be required only if isVoter is false. Otherwise will not be required.
you can use pre hook in mongoose, check the documentation and before saving, check the isVoter value, if you don't want to save promises, try this.promises = undefined
user.pre('save', function(next) {
if(this.isVoter== true){
this.promises = undefined
}
else{
this.promises = "hello"
//do somethings
}
next();
});
and in the schema promises should be definded
I'm trying to add a new user to the users' table that I have in PostgreSQL database. there is no problem when I use findOne() method but when I use create() nothing happens. here is some of my configuration:
model(users.js)
var bcrypt = require('bcryptjs');
module.exports = {
tableName: "users",
attributes: {
id: {
type: 'number',
unique: true,
autoIncrement: true,
},
first_name: {
type: 'string',
required: true
},
last_name: {
type: 'string',
required: true
},
email: {
type: 'string',
required: true,
unique: true,
isEmail: true
},
password: {
type: 'string',
required: true
},
},
}
usersController.js
registerUser: function (req, res) {
bcrypt.hash(req.body.password, 10, function(err, hash) {
if (err) return res.send('An error occured', 500);
console.log(hash);
// Save user to the database
Users.create({
first_name: req.body.first_name,
last_name: req.body.last_name,
email: req.body.email,
password: hash
}).exec(function(err, user){
if (err) return res.send('An error occured', 500);
console.log(user)
})
});
}
and i set my datastore.js file to:
default: {
adapter: require('sails-postgresql'),
url: 'postgresql://username:password#host:5432/db-name',
},
and finally, in my models.js in config folder, i set migrate:'safe' and i commented the schema and attributes.
module.exports.models = {
// schema: false,
migrate: 'safe',
attributes: {
// createdAt: { type: 'number', autoCreatedAt: true, },
// updatedAt: { type: 'number', autoUpdatedAt: true, },
// id: { type: 'number', autoIncrement: true, },
//--------------------------------------------------------------------------
// /\ Using MongoDB?
// || Replace `id` above with this instead:
//
// ```
// id: { type: 'string', columnName: '_id' },
// ```
//--------------------------------------------------------------------------
},
dataEncryptionKeys: {
default: '/ZBj9EC29Xb+mNULkmFn3NuYUK9wDLJdWQ4U2lNR7+w='
},
cascadeOnDestroy: true
};
every thing seems to be ok but i don't know what the problem is?
In a MEAN app i define my user model and encrypt the password using the fields hash and salt like these:
var mongoose = require('mongoose');
var crypto = require('crypto');
var jwt = require('jsonwebtoken');
var UsersSchema = new mongoose.Schema({
personalId: {
type: String,
unique: "Personal Id already exists",
required: true
},
name: {
type: String,
required: true
},
surname:{
type: String,
required: true
},
username: {
type: String,
unique: "Username already exists",
required: "Please fill in a username",
lowercase: true
},
hash: String,
salt: String,
email:{
type: String,
unique: true,
lowercase: true,
trim: true
},
contract:{
type: String
},
role:{
type: String,
required: true
},
dateUpdated: {
type: Date
},
dateCreated: {
type: Date,
default: Date.now
}
});
UsersSchema.methods.setPassword = function (password) {
this.salt = crypto.randomBytes(16).toString('hex');
this.hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64, 'sha512').toString('hex');
};
UsersSchema.methods.validPassword = function (password) {
var hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64, 'sha512').toString('hex');
return this.hash === hash;
};
UsersSchema.methods.generateJwt = function () {
var expiry = new Date();
expiry.setDate(expiry.getDate() + 7);
return jwt.sign({
_id: this._id,
username: this.username,
exp: parseInt(expiry.getTime() / 1000),
}, "MY_SECRET"); // DO NOT KEEP YOUR SECRET IN THE CODE!
};
mongoose.model('Users', UsersSchema);
When i create a new user, return the object user complete (all values),
when i get list of users too return all values for each user.
My question is: Is correct return salt and hash values when i ask for users objects ?
Correct or not, just make sure you're not leaking the user object in app logs. You may want to have another table though just to link users and passwords instead of having it all in the users object.
I have a schema setup like this:
var UserSchema = new Schema({
id: {type: String, required: true, unique: true, index: true, default: mongoose.Types.ObjectId},
name: { type: String, required: true },
email: { type: String, required: true, unique: true, index: true },
mobile: { type: String, unique: true, sparse: true },
password: { type: String, required: true }
});
Seems to work great except the email field is letting in duplicates, despite have unique:true set. I do the following:
User.create({
name: req.body.name,
email: req.body.email,
mobile: req.body.mobile,
password: password
}, function(err, user) {
if (err) return res.send({ invalid : true });
});
If req.body.email is a value that's already in the database, the query above should return err. But it doesn't, it creates the new user perfectly fine, resulting in duplicate emails in the database.
Why is this happening?
mongo and hence mongoose will assign an automatic id fields to your document. usually that is "_id".
Try removing the "id" from your schema to fix your issue.