I was trying to use mongoose getter to cast all user password before send out. It works perfectly.
However, on method "comparePassword", I need the passwordstring to compare sothen I can authenticate.
Is there a way to bypass the getter under certain conditions in mongoose? Thanks in advance!
Code Example:
function castpassword (pw) {
return 'keyboard cat';
}
var AccountSchema = new Schema({
password: { type: String, get: castpassword }
});
AccountSchema.methods.comparePassword = function (candidatePassword, cb) {
// random hash vs keyborad cat === not authenticated
crypt.compare(candidatePassword, this.password, function (err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
....
Account.findById( someId, function (err, found) {
console.log(found.password); // 'keyboard cat'
});
You can use mongoose 'lean' to skip all mongoose magic and just pull out a json object.
Account
.findById(someId)
.lean()
.exec(function (err, found) {
console.log(found.password); // actual password
// you can not use mongoose functions here ex:
// found.save() will fail
})
Another option would be to set password to 'select: false' in the schema.
var AccountSchema = new Schema({
password: { type: String, select: false }
});
This way anytime you pull out the document the password field would not be there at all unless you specifically as for it.
Account
.findById(someId, function (err, found) {
console.log(found.password); // undefinded
})
Account
.findById(someId)
.select('password') // explicitly asking for password
.exec(function (err, found) {
console.log(found.password); // actual password
})
Using this.toObject() in mongoose will bypass all getter and setter settings in mongoose since it change it to plain JSON data
AccountSchema.methods.comparePassword = function (candidatePassword, cb) {
// keyboard cat vs keyboard cat === authenticated
crypt.compare(candidatePassword, this.toObject().password, function (err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
Related
(Assume the hashing has been done) I am trying to do an authenticate user function, by comparing an entered password and its hash in my MongoDB collection. This is my the method in my model.js (copied from the bcrypt guide):
PetOwnerSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
And my controller.js:
exports.check = function (req, res) {
var email = req.body['email'];
var password = req.body['password'];
PetOwner.findOne({ "email" : email}, 'email', function(err, task) {
if (err)
res.send(err);
if ( task === null ){
res.json(0); // no matching result
console.log("Email is not yet registered");
} else {
task.comparePassword(password, task.password, function(err, isMatch) { //compare password and the already-hashed password in MongoDB
if (err) throw err;
if(isMatch){
res.json(1);
console.log("Found matching password to email");
}
else{
res.json(0);
console.log("Wrong password");
}
});
}
})
};
And when I fetch the check method, the node console prompts the error that the cb in my model.js is not a function. I have tried several workaround but none has worked so far. Is there any way to debug this?
PetOwnerSchema.methods.comparePassword = function(candidatePassword, cb)
Your function takes a single parameter so you cannot refer to a context of "this" outside the function, like you do by using "this.password".
If you add the password to compare with as a second parameter like so:
PetOwnerSchema.methods.comparePassword = function(candidatePassword, password2, cb)
You can then just compare the two inside the function as you are trying to do.
Hope it helps
I'm using Node.js and passport facebook strategy to log client in app.
I followed the passport docs but still have an error: Data must be a string or a buffer.
Strategy redirects to facebook page very well, but after test accepts app's conditions and it redirects to homepage, the app throw this error:
StatusCodeError: 500 - {"error":"Data must be a string or a buffer"}
This is my code from auth.js where strategy is written. I'm using jsonwebtoken module to sign user id.
exports.facebookStrategy = new FacebookStrategy({
clientID: config.auth.facebook.clientID,
clientSecret: config.auth.facebook.clientSecret,
callbackURL: config.auth.facebook.callbackURL,
profileFields: ['id', 'displayName', 'email']
}, function (accessToken, refreshToken, profile, done) {
var userProfile = {
username: profile._json.id,
name: profile._json.name,
email: profile._json.email,
facebook: true
}
findOrCreate(userProfile, (err, user) => {
if (err) {
return done(err);
}
// use token lib to sign id
jwt.sign({ userId: user.username }, config.secret, {}, (e, token) => {
if (e) {
return done(e);
}
user.token = token;
return done(null, user);
})
});
function findOrCreate (user, callback) {
client.getUser(user.username, (err, usr) => {
if (err) {
return client.saveUser(user, callback);
}
callback(null, usr);
})
}
});
Using a console.log I figured out that error comes from this code of block:
...
findOrCreate(userProfile, (err, user) => {
if (err) {
console.log(err.message); // it returns 500 - {"error":"Data must be a string or a buffer"}
return done(err);
}
I tried to change profile._json to profile._raw. However all values are undefined.
I'm using Node 6.10.0 version. and passport: "^0.3.2", "passport-facebook": "^2.1.1".
How can I solve this error?
This error ocurr when function to create password encrypted the parameter is null. By example:
const crypto = require('crypto')
let shasum = crypto.createHash('sha256')
shasum.update(password) // password is null
This authentication method is not require password you must code a conditional to prevent the encryption.
I want to connect to different databases on server side so I can perform queries that include those two databases using node.
I have a config.js like this:
module.exports = {
database: {
user: 'brunojs',
password: 'bdpf5',
connectString: 'localhost:1521/orcl'
},
jwtSecretKey: "jmvhDdDBMvqb=M#6h&QVA7x"
};
This saves my info for accessing the first database.
Then I have one list.js file which performs the query:
var oracledb = require('oracledb');
var jwt = require('jsonwebtoken');
var config = require(__dirname + '../../config.js');
function get(req, res, next) {
oracledb.getConnection(
config.database,
function(err, connection){
if (err) {
return next(err);
}
connection.execute(
'select num_sequencial, notes, des_especialidade, dt_diag ' +
'from organite_repository ',
{},//no binds
{
outFormat: oracledb.OBJECT
},
function(err, results){
if (err) {
connection.release(function(err) {
if (err) {
console.error(err.message);
}
});
return next(err);
}
res.status(200).json(results.rows);
connection.release(function(err) {
if (err) {
console.error(err.message);
}
});
}
);
}
);
}
module.exports.get = get;
Everything works fine.
The thing is, right now, I want to perform queries using another database. How can I do that?
the right solution is to use pool https://github.com/oracle/node-oracledb/blob/master/doc/api.md#createpool
Creating massive pool:
module.exports = {
database: [{user: 'brunojs',
password: 'bdpf5',
connectString: 'localhost:1521/orcl',
poolAlias:'database1'
},
{user: 'brunojs',
password: 'bdpf5',
connectString: 'localhost2:1521/orcl',
poolAlias:'database2'
}],
jwtSecretKey: "jmvhDdDBMvqb=M#6h&QVA7x"
};
during initialization web-server, initialize the pools
const dbConfig = require('../config/database.js');
async function initialize() {
dbConfig.database.forEach(async function(item) {
const pool = await oracledb.createPool(item);
});
}
Then you can use the created pools when calling the connection procedure:
conn = await oracledb.getConnection('database1');
const execresult = await conn.execute(context.execsql, execbinds, context.opts);
First, add a second credentials object in your config.js
module.exports = {
database: {
user: 'brunojs',
password: 'bdpf5',
connectString: 'localhost:1521/orcl'
},
database2: {
user: 'user2',
password: 'password',
connectString: 'someotherhost:1521/orcl'
},
jwtSecretKey: "jmvhDdDBMvqb=M#6h&QVA7x"
};
then use one or other here:
oracledb.getConnection(
config.database, // you could change this line to config.database2
function(err, connection){
if (err) { ...
If you want to query one database, then another, you'd need to keep references to both connection objects (error checking omitted for brevity):
oracledb.GetConnection(
config.database,
function(err, connection1) {
oracledb.GetConnection(
config.database2,
function(err, connection2) {
// in this function you can use either connection object
connection1.execute(...);
connection2.execute(...);
}
});
This is slightly out of scope for your question, but you could also take a look at Waterline. It supports setting up multiple databases and then tying models to them, so that knowing where certain data models are stored is abstracted away.
you can always use links on the DB side, so your java code does not have to connect to another DB, for example:
select num_sequencial, notes, des_especialidade, dt_diag
from organite_repository#linkA
UNION
select num_sequencial, notes, des_especialidade, dt_diag
from organite_repository#linkB
/* ... */
/**
* User.js
*
* #description :: TODO: You might write a short summary of how this model works and what it represents here.
* #docs :: http://sailsjs.org/#!documentation/models
*/
var bcryptjs = require('bcryptjs');
function hashPassword(values, next) {
bcryptjs.hash(values.password, 10, function(err, hash) {
if (err) {
return next(err);
}
values.password = hash;
next();
});
}
module.exports = {
connection: 'mysql',
attributes: {
id:{
primaryKey: true,
autoIncrement: true,
unique: true
},
displayname:{
type: 'STRING',
required: true
},
password:{
type: 'STRING',
required: true
},
// Override toJSON instance method to remove password value
toJSON: function() {
var obj = this.toObject();
delete obj.password;
return obj;
},
},
// Lifecycle Callbacks
beforeCreate: function(values, next) {
hashPassword(values, next);
},
beforeUpdate: function(values, next) {
if (values.password) {
hashPassword(values, next);
}
else {
//IMPORTANT: The following is only needed when a BLANK password param gets submitted through a form. Otherwise, a next() call is enough.
User.findOne(values.id).done(function(err, user) {
if (err) {
next(err);
}
else {
values.password = user.password;
next();
}
});
}
},
validPassword: function(password, user, cb) {
bcryptjs.compare(password, user.password, function(err, match) {
if (err) cb(err);
if (match) {
cb(null, true);
} else {
cb(err);
}
});
}
};
The hashPassword(values, next); in beforeUpdate method changes the password while changing any values of the user model though i don't send the password value in the 'param'. But it works fine when i change password for the user.
Example: When i change password for the current user, it should changed pass, hash it and stored in database. But i don't want the password to get changed (changes to random password) when i am updating other data in User Model.
Edit: Working Now, Corrected Code:
Now, only if you send password: password in the Update method, it will update the password (hash and store) else it will only update the provided user fields.
Controller(UserController.js):
updateDisplayName: function(req, res) {
var userid = req.token;
var newDisplayName = req.param('newdisplayname');
User.update({id: userid},{displayname: newDisplayName}).exec(function afterwards(err,updated){
if (err) {
res.json(err);
} else {
res.json("Success");
}
});
},
Model(User.js):
beforeUpdate: function(values, next) {
if(values.password) {
hashPassword(values, next);
} else {
next();
}
},
beforeUpdate: function(values, next) {
if (values.password) {
hashPassword(values, next);
}
This code has the problem that everytime a user model is updated(not password change) for eg: displayName is updated. Password in user model is already encrypted, it gets encrypted again and old password wont work no more.
Solution is to remove password attribute from user model before normal update(not change password). During password change, new password has to be set to user.password and update should be called.
As far as I can see the problem is that you get the password param passed even though you're not changing it. How about doing a bcrypt.compare on it when it arrives. If it's a match then do not re-hash the password. If it doesnt match it's considered a new password and you go ahead an hash it?
I am having this conundrum myself and I am going for this approach.
I am currently working on boiler plate code of mean.io and implementing passwordresetemail to it. When ever user asks for password reset with email as parameter, I create a salt(resetid) and send him an email having that salt as reset link.
I have user's email in the req but want to append other information of the user(user._id) before it enters into actual createemail controller function. I want following function(userbyemail) to be run before it goes into createResetPasswordEmailLink
/**
* Find user by email
*/
exports.userByEmail = function(req, res, next, email) {
User
.findOne({
email: email
})
.exec(function(err, user) {
if (err) return next(err);
if (!user) return next(new Error('Failed to load User with email ' + email));
req.user = user;
next();
});
};
exports.createResetPasswordEmailLink = function(req, res) {
var resetPassword = new ResetPassword(req.body);
resetPassword.resetId = new User().makeSalt();
**resetPassword.user = req.user._id; // currently req.user is null**
console.log(resetPassword.userId);
resetPassword.save(function(err) {
if (err) {
// handle error
}
res.status(200);
return res.redirect('/');
});
};
Following is my resetPassword schema
var ResetPasswordSchema = new Schema({
created: {
type: Date,
default: Date.now
},
resetId: {
type: String,
default: '',
trim: true
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
My routes is defined as follows
// Setting up the users resetpasswordlink
app.route('/createresetpasswordemaillink')
.post(users.createResetPasswordEmailLink);
I could solve this issue with app.params. App.params is exactly defined for this kind of usage.
I added
// Setting up the userId param
app.param('email', users.userByEmail);
to my routes and it automatically executed this function before the req is passed on to regular controllers.
From the documentation, App.param is defined as
app.param([name], callback)
Map logic to route parameters. For example when :user is present in a route path you may map user loading logic to automatically provide req.user to the route, or perform validations on the parameter input.