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('/'));
Related
I have the following middleware in a Node.js REST API
const Authorized = (req, res, next) => {
if (!req.headers["authorization"]) {
res.status(401).send("Unauthorized");
} else {
jwt.verify(req.headers["authorization"], PRIVATE_KEY, (err, decoded) => {
if (err) {
res.status(403).send("Forbidden");
} else {
req.businessId = decoded.businessId;
req.roleId = decoded.roleId;
next();
}
});
}
};
As you can see I'm adding to variables to the request object
In the mockup of my tests I'm trying to do this:
sandbox = sinon.createSandbox();
sandbox.stub(Authorized, "Authorized")
.callsFake(async (req, res, next) => {
req.businessId = businessAux.id;
return next();
});
But this doesn't work and my actual function to be tested needs this variable:
listPermissionAndRolesByPk() {
this.app.get(`${config.API_PATH}/permissionrolebypk`, Authorized.Authorized, async (req, res) => {
const id = req.query.id;
const businessId = req.businessId;
if (!id) return res.status(400).send(messages[23].message.replace("${object}", "Permission Id"));
try {
const permission = await PermissionDAO.getPermissionAndRolesByPk(id, businessId ? businessId : 0);
if (permission) {
return res.status(200).json(permission);
} else {
return res.status(404).send(messages[8].message.replace("${object}", "Permission"));
}
} catch (error) {
return res.status(error.httpCode).send(error.message);
}
});
}
Any help will be appreciated.
I have made VerifyUserAccess middleware in loopback as below:
module.exports = function(options) {
return function storeCurrentUser(req, res, next) {
if (!req.accessToken) {
return next();
}
app.models.UserModel.findById(req.accessToken.userId, function(err, user) {
if (err) {
return next(err);
}
if (!user) {
return next(new Error('No user with this access token was found.'));
}
const LoopBackContext = require('loopback-context');
const loopbackContext = LoopBackContext.getCurrentContext();
if (loopbackContext) {
loopbackContext.set('currentUser', user);
}
next();
});
};
};
And Also I added it in middleware.json as below:
...
"initial": {
"./middleware/verify_user_access": {},
...
}
...
But problem is that always loopbackContext I got null.
And the second question is that how to access currentUser result in API. I have done below:
Gameversionandtype.GetGameversionandtypeByGameTypeID = (ctx, callback) =>
{
const LoopBackContext = require('loopback-context');
const context = LoopBackContext.getCurrentContext();
const currentUserResult = context && context.get('currentUserResult');
console.log('currentUser.username: ', currentUserResult.id);
...
...
}
And Please let me know that how to set currentUser result and how to get it in API. I have referred documentation as well loopback-context npm module but I can not find any solutions. Thanking you in advance.
I have the issue that my next(err) is not working. It ignores error and just loads the page instead of sending HTTP status code 404.
The ldap search works fine and result looks as expected. It just doesn't return error when the else statement is hit.
The console does show the failed in log
app.use(function(req, res, next){
conn.search('dc=foo', opts, function (err, res) {
assert.ifError(err)
var entries = []
res.on('searchEntry', function (entry) {
entries.push(entry.object)
})
res.on('end', function (result) {
conn.unbind(function (err) {
console.log('Disconnecting')
if (entries.length == 1) {
next()
} else {
console.log('fail')
var err = new Error('Permission Denied')
err.status = 404
next(err)
}
})
})
})
})
I don't think next works like that. If you do not want your next middleware to be called, don't call next(error) or next() at all. Use instead :
if (entries.length == 1) {
next()
} else {
console.log('fail')
return res.status(404).send('Permission Denied')
}
I'm using mongodb for pretty much everything in my node.js application, and now i want create a restful application, so, i did that:
I'm trying to do just the get method, for now:
restApi.js:
var restAPI = {
get: function(method, model, sort, limit, options) {
if (method !== 'get') {
return;
}
model.find(options).sort(sort).limit(3).exec(function (error, result) {
if (error) {
return error;
} else {
return result;
}
});
},
};
And now i can require this in my route:
var restApi = require('restApi');
and use like this:
app.get('/', function(req, res, next) {
var result = restAPI.get('get', Event, 'date', 3, {'isActive': true});
res.render('/', {
result: result
});
});
Is not working, the result is undefined. Why??
How can i transform this in a async function with callback? This is possible?
Thanks! :)
You're not returning anything from restApi.get. If you're using mongoose, you could return a Promise easily enough:
var restAPI = {
get: function(method, model, sort, limit, options) {
if (method !== 'get') {
return;
}
return model.find(options).sort(sort).limit(3).exec();
},
};
Then you can use it like this:
app.get('/', function(req, res, next) {
restAPI.get('get', Event, 'date', 3, {'isActive': true}).then( function ( result ) {
res.render('/', {
result: result
});
}).catch( error ) {
// Render error page and log error
});
});
It is because your model is async. You have to pass callbacks.
Using async way is better because it is not blocking your application while waiting for response.
Example on your case:
restApi.js:
var restAPI = {
get: function(method, model, sort, limit, options, cb) {
if (method !== 'get') {
return cb("Method must be GET");
}
model.find(options).sort(sort).limit(3).exec(function (error, result) {
if (error) {
return cb(error);
} else {
return cb(null, result);
}
});
},
};
And now i can require this in my route:
var restApi = require('restApi');
and use like this:
app.get('/', function(req, res, next) {
restAPI.get('get', Event, 'date', 3, {'isActive': true}, function(err, result){
if(err)
return res.render("Error:" + err)
res.render('/', {
result: result
});
});
});
I've added cb argument to your REST API function so it is called when model async operation is done.
Router handler passes it's callback and prints output when operation is finished.
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");
}
});
};