I'm trying to perform a beforeCreate operation on a model that I created via Sequelize. I'm trying to have the password and salt saved before creating the user. However, the user is created without the encrypted password or salt. I'm not too familiar with Node.JS but I'm assuming this has to do with it's asynchronous nature. Any idea how to properly introduce a callback so that my create function behaves as intended?
Model:
'use strict';
var Promise = require("bluebird");
var bcrypt =Promise.promisifyAll(require("bcrypt-nodejs"));
const SALT_ROUNDS = 10;
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('User', {
username: { type: DataTypes.STRING, unique: true, allowNull: false, validate: { notEmpty: true } },
email: { type: DataTypes.STRING, unique: true, allowNull: false, isEmail: true },
phone_number: DataTypes.STRING,
password_hash: { type: DataTypes.STRING, allowNull: false, unique: true, validate: { notEmpty: true } },
password_salt: DataTypes.STRING,
first_name: DataTypes.STRING,
last_name: DataTypes.STRING,
user_type: DataTypes.INTEGER,
two_factor_enabled: { type: DataTypes.BOOLEAN, defaultValue: false, },
email_verified: DataTypes.DATE,
active: { type: DataTypes.BOOLEAN, defaultValue: true, },
}, {
classMethods: {
associate: function(models) {
// associations can be defined here
},
validPassword: function(password, passwd, callback) {
bcrypt.compare(password, passwd, function(err, isMatch) {
if (isMatch) {
return callback(null, true);
} else {
return callback(null, false);
}
});
},
},
hooks: {
beforeCreate: function(user, {}) {
bcrypt.genSalt(SALT_ROUNDS, function(err, salt) {
bcrypt.hash(user.password_hash, salt, function(){}, function(err, hash) {
if (err) {
return sequelize.Promise.reject(err);
}
user.setDataValue('password_hash',hash);
user.setDataValue('password_salt',salt);
});
});
}
},
instanceMethods: {
generateHash: function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(10), null);
},
validPassword: function(password) {
return bcrypt.compareSync(password, this.password);
}
}
});
//User.associate = (models) => {
// User.hasMany(models.UserType, {
// foreignKey: 'userId',
// as: 'userTypes'
// });
//};
return User;
};
And here's the call:
return db.User
.create({
first_name: req.body.first_name,
last_name: req.body.last_name,
username: req.body.username,
email: req.body.email,
password_hash: req.body.password
})
.then(user => res.status(201).send(user))
.catch(error => res.status(400).send(error));
},
Personnaly I use the crypto, included in node.js : https://nodejs.org/api/crypto.html
I have a function to create the hash :
function createHash(password, salt) {
const generatedSalt = typeof salt !== 'undefined' ? salt : crypto.randomBytes(128).toString('base64');
const hmac = crypto.createHmac('sha256', generatedSalt);
hmac.update(password);
const hashedPassword = hmac.digest('hex');
return {
salt: generatedSalt,
hash: hashedPassword
};
}
And in my user model :
beforeCreate: (user, options, cb) => {
const saltAndHash = createHash(user.password);
user.salt = saltAndHash.salt;
user.password = saltAndHash.hash;
return cb(null, options);
}
I hope this will help you ;)
Related
i dont understand what should i do when i have erorr "model is not defined" when i GET the data from the postman. i'm a newbie for node.js and javascript
my dir is:
app
--models
----user.js
model
--users.js
routes
--users.js
i've the app/models/user.js is:
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class users extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
};
users.init({
name: DataTypes.STRING,
email: DataTypes.STRING,
phone_number: DataTypes.STRING,
gender: DataTypes.BOOLEAN
}, {
sequelize,
modelName: 'users',
});
return users;
};
i've model/users.js is :
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER(5)
},
name: {
type: Sequelize.STRING(30)
},
email: {
type: Sequelize.STRING(50)
},
phone_number: {
type: Sequelize.STRING(15)
},
gender: {
type: Sequelize.BOOLEAN,
defaultValue: true
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.fn('NOW'),
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.fn('NOW'),
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('users');
}
};
and i've routes/users.js is :
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', async function (req, res, next) {
try {
const users = await model.users.findAll({});
if (users.length !== 0) {
res.json({
'status': 'OK',
'messages': '',
'data': users
})
} else {
res.json({
'status': 'ERROR',
'messages': 'EMPTY',
'data': {}
})
}
} catch (err) {
res.json({
'status': 'ERROR',
'messages': err.message,
'data': {}
})
}
});
module.exports = router;
i run the node server in the postman with GET, and my result is:
{
"status": "ERROR",
"messages": "model is not defined",
"data": {}
}
You have not import the model file in this file "routes/users.js
var model = require("./app/models/user.js");
i have a strange effekt at a m:n relation..
this are the model definitions:
Role Model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Role = sequelize.define('Role', {
uuid: {
allowNull: false,
primaryKey: true,
unique: true,
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
},
....
}, {});
/** #param models.User */
Role.associate = function(models) {
Role.belongsToMany(
models.User, {
through: 'user_role',
foreignKey: 'userId',
}
);
};
return Role;
};
User Model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
uuid: {
allowNull: false,
primaryKey: true,
unique: true,
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
},
....
}, {});
/** #param models.Role */
User.associate = function(models) {
User.belongsToMany(
models.Role, {
through: 'user_role',
foreignKey: 'roleId',
}
);
};
return User;
};
the migration is the following:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('user', {
uuid: {
allowNull: false,
unique: true,
primaryKey: true,
type: Sequelize.UUIDV4,
defaultValue: Sequelize.UUIDV4,
},
....
}).then(() => {
queryInterface.createTable('role', {
uuid: {
allowNull: false,
unique: true,
primaryKey: true,
type: Sequelize.UUIDV4,
defaultValue: Sequelize.UUIDV4,
},
....
});
}).then(() => {
queryInterface.createTable('user_role', {
userId: {
type: Sequelize.UUIDV4,
references: {
model: 'User',
key: 'uuid',
},
allowNull: false,
},
roleId: {
type: Sequelize.UUIDV4,
references: {
model: 'Role',
key: 'uuid',
},
allowNull: false,
},
....
});
}).then(() => {
return queryInterface.addConstraint('user_role', ['UserId', 'RoleId'], {
unique: true,
type: 'primary key',
name: 'userrole_pkey',
});
});
},
down: (queryInterface, Sequelize) => {
....
},
};
if i try to insert now a user with a new role:
let models = require('../models');
models.Role.create({
role: 'Administrator',
description: 'Administrator Gruppe',
}).then(role => {
models.User.create({
login: 'admin',
password: '123',
nick: 'Admini',
mail: 'admin#localhost.com',
}).then(user => {
user.addRole(role);
user.save().then(() => {
console.log('admin created');
}).catch(err => {
console.log(err);
});
}).catch(err => {
console.log(err);
});
}).catch(err => {
console.log(err);
});
it tries to add the role uuid in the userid and the user uuid in the roleid.. and for that the constraint fails...
any hints or tips where i made a mistake?
found the mistake myself (with help of a college)
at
models.User, {
through: 'user_role',
foreignKey: 'userId',
}
i set the wrong foreign key, it's not the field in the helper table, it's needed to be the source table (in this case uuid of user model) or leave it blank for sequelize's default behaviour to use the primary key.
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?
I have 2 models: User and Foto
Each User has a lot of fotos, and each foto can have just 1 user related.
To do that i use include, the problem is, i can use the include just when i am querying the user and not when i query the foto.
I get there is no relationshop between User and foto problem.
So at the moment i have this:
Model User:
"use strict";
var sequelize = require('./index');
var bcrypt = require('bcrypt-nodejs');
var Foto = require('./Foto');
module.exports = function (sequelize, DataTypes) {
var User = sequelize.define("User", {
username: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isUnique: function (value, next) {
var self = this;
User.find({ where: { username: value } })
.then(function (user) {
// reject if a different user wants to use the same username
if (user && self.id !== user.id) {
return next('username already in use!');
}
return next();
})
.catch(function (err) {
return next(err);
});
}
}
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isUnique: function (value, next) {
var self = this;
User.find({ where: { email: value } })
.then(function (user) {
// reject if a different user wants to use the same email
if (user && self.id !== user.id) {
return next('Email already in use!');
}
return next();
})
.catch(function (err) {
return next(err);
});
}
}
},
typeOfUser: {
type: DataTypes.INTEGER,
allowNull:false,
defaultValue:2
},
country: {
type: DataTypes.STRING,
allowNull:true,
defaultValue:null
},
birthDate:{
type: DataTypes.DATEONLY,
allowNull:true,
defaultValue:null
},
reports: {
type: DataTypes.INTEGER,
defaultValue: 0
},
points: {
type: DataTypes.INTEGER,
defaultValue: 0
},
password: {
type: DataTypes.STRING,
allowNull:false
},
numberFotos: {
type: DataTypes.INTEGER,
defaultValue: 0
}
}, {
classMethods: {
generateHash: function (password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
},
associate: function(models){
User.hasMany(models.Foto,{foreignKey: "userId", as: "Fotos"});
}
},
instanceMethods: {
validPassword: function (password) {
return bcrypt.compareSync(password, this.password);
}
}
});
return User;
}
Model Foto:
"use strict";
var sequelize = require('./index');
var bcrypt = require('bcrypt-nodejs');
var User = require('./User');
module.exports = function (sequelize, DataTypes) {
var Foto = sequelize.define("Foto", {
reports: {
type: DataTypes.INTEGER,
defaultValue: 0
},
image: {
type: DataTypes.STRING,
allowNull: false
},
date: {
type: DataTypes.DATE,
allowNull: true
},
lat: {
type: DataTypes.STRING,
allowNull: true
},
lon: {
type: DataTypes.STRING,
allowNull: true
},
altitude: {
type: DataTypes.STRING,
allowNull: true
},
userId: {
type: DataTypes.INTEGER,
allowNull: false
},
plantId: {
type: DataTypes.INTEGER,
allowNull: true
},
},
{
associate: function (models) {
Foto.belongsToMany(models.User, {as:'Users'});
}
}
);
return Foto;
}
then i try to get something like this in a json three:
[{
FotoA:{
prop1:value1,
prop2:value2,
user:{
userProp1
}
}
FotoB:{
}
}]
on my route i do the following:
allPictures: function (req, res) {
Foto.findAll({include: [{ model: User, as: "Users",where:{userId: User.id} }]})
.then(function (fotos) {
res.send(fotos);
})
},
if there is a better way to do this instad of eager loading please share it, i just need to get the userId and the username.
Thanks
I guess you defined the association wrong, as you mentioned a Foto should belong to one User.
try
Foto.belongsTo(model.User);
instead of
associate: function (models) {
Foto.belongsToMany(models.User, {as:'Users'});
}
And also there should be no need for the where clause when selecting. If your associations are defined correctly, you can simply do
Foto.findAll({include: [models.User]})
My models:
'use strict';
module.exports = function(sequelize, DataTypes) {
var Entity;
return Entity = sequelize.define('Entity', {
name: {
type: DataTypes.STRING,
allowNull: false
}
}, {
classMethods: {
associate: function(models) {
return Entity.belongsToMany(models.User);
}
}
});
};
and
'use strict';
module.exports = function(sequelize, DataTypes) {
var User;
return User = sequelize.define('User', {
name: {
type: DataTypes.STRING,
allowNull: false
},
username: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
password: {
type: DataTypes.STRING,
allowNull: false
},
status: {
type: DataTypes.ENUM('active', 'inactive'),
defaultValue: 'active'
}
}, {
instanceMethods: {
display: function() {
var user;
user = {
name: this.name,
email: this.email,
username: this.username,
active: this.active
};
return user;
}
}
}, {
classMethods: {
associate: function(models) {
return User.belongsToMany(models.Entity);
}
}
});
};
I want to create a user and then attach one entity to that user, so I am doing:
newUser = {
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: req.body.password
};
global.db.User.create(newUser).then(function(dbUser) {
var newEntity;
newEntity = {
name: newUser.name + " Default Entity"
};
console.log(dbUser);
return dbUser.createEntity(newEntity);
}).then(function(dbEntity) {
return console.log(dbEntity);
});
But I get an error: [TypeError: Object [object SequelizeInstance] has no method 'createEntity']
This is using Sequelize v2.0.0-rc8
What am I doing wrong?
dbUser is an instance, so dbUser.createEntity() is calling the instance method createEntity() on the dbUser instance. Which doesn't exist.
The correct solution is to call create on your Entity model, with the UserId field set to dbUser.id. So, something like:
global.db.Entity.create({name: 'blah blah blah', UserId: dbUser.id})
(you may have to fiddle around with the capitalization).
Also, I think you may want User.hasMany(models.Entity), but that's just something that caught me off guard.