I'm having problems understanding the processing order in Node.js.
My Problem:
I coded a little Application that saves a session in a cookie with the following properties:
session.email = email;
session.randomHash = randomHash;
The randomHash var is a random String that gets generated and saved to a db everytime the user logs in.
If a user with a session now wants to view a private page the method checkSession() gets called:
exports.checkSession = function(req, res) {
if(req.session) {
var User = mongoose.model("User", userSchema);
User.count({email: req.session.email, randomHash: req.session.randomHash}, function(err, count) {
if(count === 0) {
return false;
}
if(count === 1) {
return true;
}
});
}
else {
return false;
}
};
The method compares the randomHash of the cookie with the randomHash value of the Db.
This method is called in a route:
exports.init = function(req, res) {
if(hashing.checkSession(req, res)) {
res.render("controlpanel", {title: "Controlpanel", email: req.session.email});
}
else {
res.send("Sorry but you are not logged in. Go to /login");
}
};
Now there must be the problem.
Because of Nodes non-blocking style the method gets called but doesn't finish before the if-statement is executed.
What can i do about it?
The return value in your User.count callback is not the return value of checkSession. The User.count callback doesn't run until after checkSession has finished.
Pass a callback to checkSession and call it in User.count:
exports.checkSession = function(req, res, callback) {
if(req.session) {
var User = mongoose.model("User", userSchema);
User.count({email: req.session.email, randomHash: req.session.randomHash}, function(err, count) {
if(count === 0) {
callback(false);
}
if(count === 1) {
callback(true);
}
});
}
else {
callback(false);
}
};
And call it like:
exports.init = function(req, res) {
hashing.checkSession(req, res, function(result) {
if(result) {
res.render("controlpanel", {title: "Controlpanel", email: req.session.email});
}
else {
res.send("Sorry but you are not logged in. Go to /login");
}
});
};
Related
I am trying to validate the user before accessing the content. I have declared function globally and passing it inside the body. When I run it , I am getting error has: user doesn't exist . Please let me know where I am going wrong.
function validate(user_id){
var user_id = db.query('select user_id from user WHERE user_id = ?', [user_id],
function(error,rows) {
if (user_id != user_id) {
return false;
} else {
return true;
}
});
}
router.post('/addcustomerskills', function(req,res) {
if (validate(user_id == true)) {
return true;
// my code should execute
}else {
response.success= false;
response.mssg = "User Doesn't Exist";
res.json(response);
}
function validate(user_id, req, res){
db.query('select user_id from user WHERE user_id = ?', [user_id], function(error,rows) {
if (error || rows.length === 0) {
var response = {}
response.success= false;
response.mssg = "User Doesn't Exist";
res.json(response);
} else {
// my code should execute
return true;
}
});
}
router.post('/addcustomerskills', function(req,res) {
validate(req.body.user_id, req, res);
})
I am working on a middleware function that should check in a db if the logged in user has the role = 2 before allowing access to the requested page. If the user does not have the role = 2 he should be redirected to the home page (/). I wrote following function to achieve that:
isAdmin = function(req, res, callback) {
let Users = require('./models/users');
Users.findOne({ 'steam.id': req.user.steam.id }, 'role', function(err, data) {
if(err) {
return callback(err, false);
} else {
if(data.steam.role === undefined || data.steam.role != 2) {
return callback(null, false);
} else {
if(data.steam.role === 2){
return callback(null, true);
}
}
}
});
};
The following function gets placed in the app.get(/admin) part of my routes file
function ensureAdmin(req, res, next) {
if (isAdmin()) {
return next();
}
console.log(colors.red('[ATTENTION] ') + colors.red('A non admin tried to access admin-only pages'));
res.redirect('/');
}
app.get:
app.get('/admin', ensureAuthenticated, ensureAdmin, function(req, res){
res.send('Admin panel!');
});
When I try to access the /admin page I just get a ReferenceError: isAdmin is not defined. Possibly there are more errors after this one that I can't solve so it would be great if anyone could tell me what I did wrong and fix the code if he wants. I am a node.js beginner :)
EDIT (new code):
module.exports = function(isAdmin){
var isAdmin = function(req, res, callback) {
if(req.user.steam.role === undefined || req.user.steam.role != 2){
return callback(null, false);
} else {
if(req.user.steam.role === 2){
return callback(null, true);
}
}
};
}
.
let isAdmin = require('../functions/isAdmin');
function ensureAdmin(req, res, next) {
if(isAdmin()) {
return next();
}
}
Do an export on your function isAdmin if you are in different files and do return that function as it's async
var isAdmin = function(req, res, callback) {
let Users = require('./models/users');
return Users.findOne({ 'steam.id': req.user.steam.id }, 'role', function(err, data) {
if(err) {
return callback(err, false);
} else {
if(data.steam.role === undefined || data.steam.role != 2) {
return callback(null, false);
} else {
if(data.steam.role === 2){
return callback(null, true);
}
}
}
});
};
export default isAdmin
Also, the call needs to be thenable
function ensureAdmin(req, res, next) {
isAdmin().then(response => {
next();
});
}
I noticed that you have written console.log res.redirect which will not make sense after calling next() in middleware. You can shift this console.log() prior to the next() call. Avoid res.redirect() in middleware
Last, Assuming that you are doing an import of a file as well as mentioned by #hansmaad
Frist you have to export your isAdmin function from the file where it is implemented
export default isAdmin
and then require it in the file where you want to use it
const isAdmin = require('../middlewares/isAdmin'); // wherever you've put this file
As your isAdmin function is async and returns a promise, you have to call next() when this promise resolved.
isAdmin().then(() => next(), () => res.redirect('/'));
I'm bulding a node.js backend for a webapp and when i submit a form, i do various validations. One of them is to check if an invitation already exists with the same email address.(isUserAlreadyInvited function)
I created a function for this, however when i call this, i guess the response is not that fast and it just moves to the next statement even if the check returns true. How
//Loop through emails one by one
var emails_to_invite = ["test#test.com","invalid.com"];
var response_items = [];
async.each(emails_to_invite,
function(email, callback){
//Response item
var response_item = {}
response_item.email = email;
//Validate
if(Joi.validate({ email: email }, apiSchema).error) {
response_item.error = "not valid email";
response_items.push(response_item);
callback();
} else if(email == user.email) {
response_item.error = "Sending an invitation to your own email is kinda dumb";
response_items.push(response_item);
callback();
} else if(isUserAlreadyInvited(email,user_id)) {
response_item.error = "already invited";
response_items.push(response_item);
callback();
} else {
sendInvitationEmail(email,user_id,null,null,function(invitation_code){
if(invitation_code) {
response_item.invitationCode = invitation_code;
} else {
response_item.error = "server error";
}
response_items.push(response_item);
callback();
});
};
},
function(err){
//Return response
res.status(200).send(response_items);
}
);
function isUserAlreadyInvited(email,invited_by) {
User.findOne({
email: email,
invited_by: invited_by
}, function(err, user) {
if(err) {
return false;
} else {
return true;
}
});
}
Two returns here:
function isUserAlreadyInvited(email,invited_by) {
User.findOne({
email: email,
invited_by: invited_by
}, function(err, user) {
if(err) {
return false;
} else {
return true;
}
});
}
are defined inside this function
function(err, user) {
if(err) {
return false;
} else {
return true;
}
}
and have nothing with return value of isUserAlreadyInvited().
Apparently your isUserAlreadyInvited() is an asynchronous function that needs to be treated asynchronously - it shall get callback function as a parameter. That callback shall be invoked in place of that function(err, user) {}
So I'm trying to create a sign up route that checks to see if the user exists first and i have the database call in a separate function that needs to return true or false when it's done. The problem is i'm not very familiar with callbacks and the whole asynchronous thing everything that i have searched for does not seem to work keeps giving me.
TypeError: callback is not a function
This is my code any help or direction would be appreciated.
function pullUserFromDatabase(username, callback) {
console.log(username); //for debug
mongodb.connect(url, function(err, db) {
if(err) {
console.log("didn't get far" + err) //for debug
}
var collection = db.collection(username);
collection.findOne({username}, function(err, item) {
if(err) {
console.log("nope it broke" + err) //for debug
} else {
console.log("it worked" + JSON.stringify(item)) //for debug
callback(true);
}
});
});
}
app.post("/signup", function(req, res) {
var username = req.headers["username"],
password = req.headers["password"],
randomSalt = crypto.randomBytes(32).toString("hex"),
passwordHashOutput = crypto.createHash('sha256').update(password + randomSalt).digest("hex");
if(!username || !password) {
res.send("Username or password not provided.")
} else if(pullUserFromDatabase(username)) {
res.send("User exist.")
}
});
You need to use the callback as follows:
function pullUserFromDatabase(data, callback) {
console.log(data.username); //for debug
mongodb.connect(url, function(err, db) {
if(err) {
console.log("didn't get far" + err) //for debug
}
var collection = db.collection(data.collection);
collection.find({"username": data.username}).count(function (err, count) {
callback(err, !! count);
});
});
};
app.post("/signup", function(req, res) {
var username = req.headers["username"],
password = req.headers["password"],
randomSalt = crypto.randomBytes(32).toString("hex"),
passwordHashOutput = crypto.createHash('sha256').update(password + randomSalt).digest("hex");
if(!username || !password) {
res.send("Username or password not provided.")
}
var data = {
username: username,
collection: "collectionName"
}
if(!username || !password) {
res.send("Username or password not provided.")
}
pullUserFromDatabase(data, function(err, exists) {
if (err) {
res.send(400, "Error - " + err);
}
else if(exists) {
res.send(200, "User exists.");
}
res.send(200, "User does not exist.");
});
});
The reason that callback is undefined is because you didn't pass a 2nd argument to pullUserFromDatabase(username) Provide a 2nd argument, eg. pullUserFromDatabase(username, function(result) {/* do something here with the result variable */})
If you're not very familiar with aync & callbacks, you might find it more intuitive to use promises, but that comes with its own learning curve.
In the context of the original code, this looks like:
...
if(!username || !password) {
res.send("Username or password not provided.");
return;
}
pullUserFromDatabase(username, function(result) {
if(result) {
res.send("User exist.");
} else {
// TODO: Handle this case. If res.send() is never called, the HTTP request won't complete
}
});
...
Also, you need to ensure your callback is always invoked. Add callback(false):
console.log("nope it broke" + err); //for debug
callback(false);
Do a similar step after "didn't get far" and then return so the callback doesn't get invoked multiple times.
How to set session value in callback? Why it doesn't work?
app.get('/room/:id', function(req, res) {
var room_id = req.param('id');
room.getRoom(room_id, function(err, result) {
if(result.length) {
req.session.code_room = room_id;
}
});
res.render('room.jade');
});
You probably should move res.render to the inside of callback function:
app.get('/room/:id', function(req, res) {
var room_id = req.param('id');
room.getRoom(room_id, function(err, result) {
if(result.length && !err) {
req.session.code_room = room_id;
} else {
//sorry...
req.session.code_room = -1;
}
res.render('room.jade');
});
});