How to use app.method() to do authorization - node.js

I am trying to use express then next('route') but I am not finding much in the docs on how to do this, it is mentioned here. But it doesn't explain how to do the
will work only in middleware functions that were loaded by using the app.METHOD() or router.METHOD() functions.
I don't get what they mean by that because I have never used app.METHOD() before and again am unable to find docs on how to do it properly with the next('route'). Would this work?
app.method('*', (req, res, next) => {
if(req.user == null) {
next();
} else {
User.findOne({"_id": req.user.id}, function(err, result) {
if(err){
console.log(err);
} else {
if(stripe.active == false && stripe.trial == false){
res.render('/dashboard/billing');
next('route');
} else {
next();
}
}
});
}
});
Would that even work? I am guessing somewhere in there I messed up something. If I did that right then it would find a user, then check is both is_active and is_trial are false and if so skip onto the next route. The idea of me doing this is so that a use can any part of the site that doesn't have a logged in user then when there is a logged in user and both are false I only let them go to /dashboard/billing. This was suggested to me as a way to prevent users who have not paid yet their trial is over from accessing my application.
Route I am testing on:
// dashboard
app.get('/dashboard',
setRender('dashboard/index'),
setRedirect({auth: '/login'}),
isAuthenticated,
(req, res, next) => {
},
dashboard.getDefault);

First of all, change the method to the right HTTP method request. In the documentation, it specifies what .METHOD() represents:
app.METHOD() functions, where METHOD is the HTTP method of the request
that the middleware function handles (such as GET, PUT, or POST) in
lowercase.
In other words, .METHOD() is a place holder for .get, .post, .put, etc.
Try changing that in your app:
app.get('*', (req, res, next) => {
if(req.user == null) {
next();
} else {
User.findOne({"_id": req.user.id}, function(err, result) {
if(err){
console.log(err);
} else {
if(stripe.active == false && stripe.trial == false){
res.render('/dashboard/billing');
next('route');
} else {
next();
}
}
});
}
});
See if you can work it out from there.
Update
Calling .next('route') right after res.render() cancels res.render() because .next('route') will send the request to the next router.

Related

website timeout after adding a .inv to req,session

I am trying to add a unique ID to my req.session but when the function executes if I want to go to another page it timeout due to infinite loading. Is there a way to do this correctly?
app.use(function (req, res, next) {
if (req.query.inv) {
sql.query(`SELECT * FROM inv WHERE inv='${req.query.inv}';`, (error, result) => {
if(error) console.log(error);
if(result.length < 1) {
req.session.inv= '';
next()
} else {
req.session.inv = `?inv=${req.query.inv}`;
console.log(req.session.inv);
next()
}
});
} else {
if(!req.session.inv) {
req.session.inv= '';
next()
}
}
});
You have a middleware which must call next() when complete so that the next middleware in the stack can be called. See Express's Using middleware documentation.
Take a look at your logic - if inv is not in your query string but does exist in your session, then next() is never called. This aligns with the issue you are having - you add inv to your session, and then on your next page load you will be forever stuck in your middleware. You would instead want logic like this:
app.use(function (req, res, next) {
if (req.query.inv) {
/* .... */
} else if (!req.session.inv) {
req.session.inv= '';
}
next(); // this makes sure that next() always gets called
});
You also have a glaring SQL Injection risk because you are taking a raw query string value and passing it directly into your query. What you want is a parameterized query - I linked to node-postgres documentation even though I'm not sure what database you are actually using. As of now your query is not safe.

What is next() doing in this Express route and why is its order in the code important?

I'm getting confused with next(); I read through this post which essentially says it passes control to the next route. In my code below, having next(); where it is causes me to get "Cannot set headers after they are sent to the client". However, if I comment that out and then restore the else clause of my if statement, it functions correctly both when an incorrect empID is passed as well as when a correct one is. I'm hoping someone could explain what exactly is happening? Why does the position of the next() matter? It seems like it would be called either way?
I'm trying to do what is happening in this post which is add a value to, say req.user, but I haven't been able to get that to work at all so I'm trying the method I have here.
let checkEmp = (req, res, next) => {
db.get("select * from Employee where id = $id", {$id: req.empID},
(err, row) => {
if (err || row === undefined) {
res.status(404).send();
// } else {
// next();
}
});
next();
};
// get all timesheets
timesheetRouter.get("/", getParams, checkEmp, (req, res, next) => {
if (req.empID) {
db.all("select * from Timesheet where employee_id = $id", {$id: req.empID},
(err, rows) => {
if (err) {
next(err);
} else {
return res.status(200).send({timesheets: rows});
}
});
} else {
return res.status(404).send("Nothing to see here");
}
});
Looks like db.get() is probably asynchronous, so in the example as shown, next() will be called before db.get() finishes and it moves on to the next handler. Then, when the db.get() finishes, it tries to send a response, but the response has already been sent by the anonymous function in the main handler. By moving the next() inside of db.get(), you're essentially waiting for it to finish before moving on.

Express Router.use in middleware

I'm new to Express.
I am trying to route a request according to a value in DB. To do this I am calling a service function.
Routing is working successfully but I am loosing the request object.
I have tried to pass the req to the service but it didn't work.
Any help showing how to do this would be nice.
Here is my code block.
var companyService = require("services/companyService");
router.use('/', function (req, res, next) {
companyService.isCompanyOnline(req.body.companyCode).then(function (company) {
if (company) {
router.use('/', require("api/controllers/online"));
}
else {
router.use('/', require("api/controllers/offline"));
}
});
next();
});
module.exports = router;
Services.companyService:
function isCompanyOnline(code) {
var deferred = Q.defer();
companies.findOne({ companyCode: code }, function (err, company) {
if (err) deferred.reject(err.name + ': ' + err.message);
//if (err) throw err;
if (company) {
// return company online parameter
deferred.resolve(company.isOnline);
} else {
// company not found
deferred.resolve();
}
});
return deferred.promise;
}
You are losing the request object because you are not passing it anywhere.
I believe your main issue here is the fact that you have three route handlers registering on the same path /. If I am not mistaken they will be all called in-order that you add them. But the order in your depends on the if you are placing the router.use() calls. So it's unpredictable, and likely not going to work as you expect. Once they are registered they stay in the middleware stack until you restart the app.
I suggest you refactor to have the online/offline logic in one place, or register all your middlewares at once, so you know in which order they get called, and use next() accordingly.
On another note, if you want to pass an argument to required modules, do this:
Change api/controllers/online and the other, to accept an argument, and return the handler function that you are passing.
// your code in api/controllers/online and offline
module.exports = function (req) {
// now you have req available in-scope here
function yourFunctionThatNeedsReq (req) {
// do stuff with req
}
return yourFunctionThatNeedsReq;
};
Then update your require like so. Note the …(req).
router.use('/', require("api/controllers/online")(req) );
My solution:
First, define routes:
app.use('/api/online', require("api/controllers/online"));
app.use('/api/offline', require("api/controllers/offline"));
Then change the url in the router :
if (req.body.companyInfo.isOnline) {
req.url = '/online' + req.url + '/' + req.body.companyInfo.companyPath;
next();
}
else {
req.url = '/offline' + req.url + '/' + req.body.companyInfo.companyPath;
next();
}

passport conditionally authenticate: Route.post() requires callback functions but got a [object Undefined]

ok i had a piece of code doing login using passport. Now based on different linux distribution i want to do it differently.
function loginDispatch(osType) {
if(osType == "sles") {
passport.authenticate('myWorkingAuth');
}
else if(osType == "ubuntu") {
passport.authenticate('local');
}
else {
console.log("error");
}
}
app.post('/login', loginDispatch(osInfo), function (req, res, next) {
next();
}, function(req, res) {
logger.trace('login called with user = ' + req.user.name);
//save the user in our session
req.session.user = req.user;
// ..............
res.send(req.session.user);
});
But it gives such error in Ubuntu:
Error: Route.post() requires callback functions but got a [object Undefined]
How to fix it please ? Thanks !
You are passing the result of loginDispatch as middleware. loginDispatch therefore needs to return a function. Amend your function to return the appropriate passport middleware:
if(osType == "sles") {
return passport.authenticate('myWorkingAuth');
}
else if(osType == "ubuntu") {
return passport.authenticate('local');
}
else {
console.log("error");
}
As an aside, you probably want to handle the final else a little more robustly, but I'm assuming this is test code and you know that :)

How to get the current User inside a 'before save' operation hook?

loopback.getCurrentContext() is null for me. Why? I need to grab the current User from the context. The operation hook's ctx is not the same context and does not contain what I need as far as I can tell.
Customer.observe('before save', function checkPermission(ctx, next) {
//do I have a loopback context that works?
var context = loopback.getCurrentContext();
console.log("context is: " + context);//null!
});
Thanks
I know this question is old but this might help some people
according to loopback doc
https://loopback.io/doc/en/lb3/Using-current-context.html
In LoopBack 2.x, this feature is disabled by default for compatibility reasons. To enable, add
first you need to add this to your customer model
"injectOptionsFromRemoteContext": true
then:
Customer.observe('before save', function checkPermission(ctx, next) {
//ctx.options returns tokenid, userid & ttl
console.log(ctx.options);
return next();
});
This isn't PostgreSQL specific, but it is a bug.
https://github.com/strongloop/loopback/issues/878#issuecomment-128417677
I think you should add a pre-processing middleware to populate the context with the current user.
/server/server.js
app.use(loopback.context());
app.use(loopback.token());
app.use(function setCurrentUser(req, res, next) {
if (!req.accessToken) {
return next();
}
app.models.Customer.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.'));
}
var loopbackContext = loopback.getCurrentContext();
if (loopbackContext) {
loopbackContext.set('currentUser', user);
}
next();
});
});
/common/models/customer.js
var loopback = require('loopback');
module.exports = function(Customer) {
Customer.observe('before save', function checkPermission(ctx, next) {
var context = loopback.getCurrentContext();
var currentUser = context && context.get('currentUser');
console.log(currentUser);
next();
});
};

Resources