Sails.js authentication for model actions - node.js

I'm making an API that has different levels of access, the 'client' may only read. But 'admin' must have write access. The different roles are check every time as a policy in Sails.js and sets the rights in the req.session.
I just need to give the 'client' no access to create, update and delete actions, therefore i created an controller that has those CRUD actions and checks if the user has the right role. All of the actions that has restricted access are redirected trough routes.js to this controller.
Now my problem is that when i'm deleting an entry like: Category.destroy(req.param('id')); Gives me undefined and has no done method. Unlike mentioned by the docs I managed to fix the problem by creating this:
var deleted = Category.destroy(req.param('id'), function(err, status) {
if (status == 1){
res.json({message: 'Category is deleted'});
} else {
res.json({message: 'Oops, something went wrong'});
}
});
But there has to be another way to apply authentication to those basic actions. Because now i've to write all actions.
Is there something wrong with the code for the delete function that i wrote? And is it possible to apply policies and redirect to default model actions, as if there was no authentication at all?

You can define policies at the Models or Controllers level. Here's an example from the /config/policies.js.
module.exports.policies = {
// Default policy (allow public access)
'*': true,
'events': 'eventsPolicy', // Policy for a Model
someController: { // Policy for a Controller
// Apply the "authenticated" policy to all actions
'*': 'authenticated',
// For someAction, apply 'somePolicy' instead
someAction: 'somePolicy'
}
};
Under the api/policies is where you can define the access level.
module.exports = function (req, res, next) {
if (req.session.user) {
var action = req.param('action');
if (action == "create") {
req.body.userId = req.session.user.id;
req.body.username = req.session.user.username;
}
next();
} else {
res.send("You're not authenticated.", 403);
}
};
Hope this helps.

Just modified all the policies en renamed the controllers, as stated in de CLI: 'sails generate model example' gives an notification about the controller being named as singular. So I didn't need to redirect all model actions to the plural controller (examples). Now all the basic CRUD actions are working as it should.
The sails.js video tutorial helped me a lot: http://www.youtube.com/watch?feature=player_embedded&v=GK-tFvpIR7c

My guess (not a Sails user myself) would be you either pass a callback, or you'll get an object back which has a done() method:
Category.destroy(id, function(...) {...}); // method 1
Category.destroy(id).done(function(...) {...}); // method 2

Related

Node.js resource based ACL

I am implementing a simple Access Control system in Node, and I am wondering what can be the best approach for what I am doing.
I am using Node ACL and it is not clear to me how to block on a per-resource basis.
Let's take the following example:
USER ->* PROJECT ->* ENTRY. Users can have multiple projects which contains many entries. Users can be ADMIN or USER.
I created an endpoint /entry/{ID} where user can access an entry detail. The endpoint is accessible to everyone, ADMINs can see all entries, but for User I need to do something similar:
app.get('/entry/{id}', (req, res) => {
if (user.admin) {
// Return eveything
}
else {
if (entry.project == user.project) {
// return it
}
else {
// Unathorized
}
}
})
Is there a better approach/pattern to implement this checks on ownership on a resource?
It's a very broad question so I'll try to give you a couple hints as my answer, but
Is there an ACL pattern in javascript?
There's a number of solutions but I wouldn't call any of those a pattern. I'll be very subjective now, but the ways of passport.js and similar modules are non-transparent to say the least - and it's not really an ACL...
Someone may say - hey, it's node.js, there must be module to do that and make your node_modules heavier but searching for a good acl module in npm, I only found some outdated ones and tightly bound with express. Since your question wasn't which is the best npm module for acl I gave up looking for such at page 3, which doesn't mean there ain't something ready so you may want to look more closely.
I think your implementation could be considered acceptable, with some minor corrections or hints as I mentioned:
Separate your request logic from access control logic
In your code everything happens in one callback - that's definitely very efficient, but also very hard to support in longer term. You see, it'll end up in the same code in lots of those if's above in all the callbacks. It's very simple to separate the logic - simply implement the same path in two callbacks (they'll be run in the order they were defined), so:
app.all('/entry/{id}', (req, res, next) => {
const {user, entry} = extractFromRequest(req);
if (user.admin || entry.project === user.project) {
next();
} else {
res.status(403).send("Forbidden");
}
});
app.get('/entry/{id}', (req, res) => {
// simply respond here
})
This way the first callback checks if the user has access and this won't affect the logic of the response. The usage of the next() is specific to express-like frameworks which I assumed you use looking at your code - when you call it the next handler will be executed, otherwise no other handlers will be run.
See Express.js app.all documentation for an acl example.
Use a service wide acl
It's much more secure to keep a basic ACL in a single place and not to define it per path unless necessary. This way you won't omit one path and won't leave a security hole somewhere in middle of request. For this we need to split the ACL into parts:
URL access check (if path is public/open for all users)
User and session validity check (user is logged in, session is not expired)
Admin/user check (so permission level)
Otherwise we don't allow anything.
app.all('*', (req, res, next) => {
if (path.isPublic) next(); // public paths can be unlogged
else if (user.valid && user.expires > Date.now()) next(); // session and user must be valid
else if (user.admin) next(); // admin can go anywhere
else if (path.isOpen && user.valid) next(); // paths for logged in users may also pass
else throw new Error("Forbidden");
});
This check is not very restrictive but we won't need to repeat ourselves. Also notice the throw Error at the bottom - we'll handle this in an error handler:
app.use(function (err, req, res, next) {
if (err.message === "Forbidden") res.status(403).send("Forbidden");
else res.status(500).send("Something broke");
})
Any handler with 4 arguments will be considered an error handler by Express.js.
On the specific path level if there's any need for ACL's, simply throw an error to the handler:
app.all('/entry/{id}', (req, res, next) => {
if (!user.admin && user.project !== entry.project) throw new Error("Forbidden");
// then respond...
});
Which reminds me of another hint...
Don't use user.admin
Ok, fine, use it if you like. I don't. The first attempt to hack your code will be by trying to set admin on any object that has properties. It's a common name in a common security check so it's like leaving your WiFI AP login at factory defaults.
I'd recommend using roles and permissions. A role contains a set of permissions, a user has some roles (or one role which is simpler but gives you less options). Roles may be also assigned to project.
It's easily a whole article about this so here's some further reading on Role-based ACL.
Use standard HTTP responses
Some of this mentioned above, but it's a good practice to simply use one of standard 4xx HTTP code status as response - this will be meaningful for the client. In essence reply 401 when the user is not logged in (or session expired), 403 when there's no sufficient priviledge, 429 when use limits are exceeded. more codes and what to do when the request is a teapot in Wikipedia.
As to implementation itself I do like to create a simple AuthError class and use it to throw errors from the app.
class AuthError extends Error {
constructor(status, message = "Access denied") {
super(message);
this.status = status;
}
}
It's really easy to both handle and throw such an error in the code, like this:
app.all('*', (req, res, next) => {
// check if all good, but be more talkative otherwise
if (!path.isOpen && !user.valid) throw new AuthError(401, "Unauthenticated");
throw new AuthError(403);
});
function checkRoles(user, entry) {
// do some checks or...
throw new AuthError(403, "Insufficient Priviledges");
}
app.get('/entry/{id}', (req, res) => {
checkRoles(user, entry); // throws AuthError
// or respond...
})
And in your error handler you send your status/message as caught from your code:
app.use(function (err, req, res, next) {
if (err instanceof AuthError) res.send(err.status).send(err.message);
else res.status(500).send('Something broke!')
})
Don't reply immediately
Finally - this is more of a security feature and a safety feature at the same time. Every time you respond with an error message, why not sleep a couple seconds? This will hurt you in terms of memory, but it will hurt just a little and it'll hurt a possible attacker a lot because they wait for the outcome longer. Moreover it's super simple to implement in just one place:
app.use(function (err, req, res, next) {
// some errors from the app can be handled here - you can respond immediately if
// you think it's better.
if (err instanceof AppError) return res.send(err.status).send(err.message);
setTimeout(() => {
if (err instanceof AuthError) res.send(err.status).send(err.message);
else res.status(500).send('Something broke!')
}, 3000);
})
Phew... I don't think this list is exhaustive, but in my view it's a sensible start.

loopback remote method return variable other than request data

I have a generic SendMail route which I want to create multiple remote methods to handle multiple request templates. Any ideas on how to return a Email_Type from the remote method back to the base route. I know I could add a default with a code in it, but would like a more elegant solution.
Mail.genericSendMail = function genericEmail(response, callback) {
console.log(response);
let templateId=0;
//PROBLEM: HOW TO KNOW WHICH REMOTE WAS USED
switch (response.emailType) {
case "Template-1":
templateId= 1234;
break;
case "Template-2":
tempalteId = 456;
break;
default:
templateId = 789l
} //switch
console.log(templateId);
};
//Want multiple routes like this to support various templates
Mail.remoteMethod("genericEmail", {
http: {
path: "/emailTemplate1",
verb: "POST"
},
accepts [
{arg: "request", type:"object",http: {source:"body"},
default: {firstName:"", lastName:"",emailAddress:""}
}],
returns: RESTResponseStatic.loopbackAdapterCommonRestResponseDefinition()
});
//Want multiple routes like this to support various templates
Mail.remoteMethod("genericEmail", {
http: {
path: "/emailTemplate2",
verb: "POST"
},
accepts [
{arg: "request", type:"object",http: {source:"body"},
default: {emailAddress:"", promoCode:""}
}],
returns: RESTResponseStatic.loopbackAdapterCommonRestResponseDefinition()
});
There are a couple of different ways to do this. Since it happens to be a POST request, I usually go with attaching data to the body using a before remote hook.
Let's say you have a model method for logging in users.
Say we have a multi realm platform, so we need to know what platform we are logging in. If you don't use realms or don't know what they are, don't worry. This just shows you how to populate the data to the model method.
User.login = function(data, cb) {
if (data.realm == 'platform1) {
return logUserIntoPlatform1(data, cb);
}
return logUserIntoDefaultPlatform(data, cb);
}
Now let's say you don't want the client/frontend to send the realm and you don't want to do the lookup for realm in the model. We can add a beforeRemote hook like so:
User.beforeRemote('login', function (context, user, next) {
context.args.data.realm = lookUpRealmSync(context); // 1
next();
});
This will be called before the login method. Note the next() call: this is how you could do error detection before actually hitting the model method. Something like next({ status: 422, message: 'Parameter missing: password }); would return an error and not execute the User.login method.
You may have to look carefully at your context object (i.e. the line marked with 1 may not work exactly as I've shown for you).
If you want to read more about this stuff, I LoopBack's docs are pretty good. It seems they've been updated since I've last used them so I can't link you to the more useful pages. I found the remote method documentation here though.
Edit: I took a closer look at your question. You should be able to retrieve the path from the context object and pass data accordingly. I'm not going to try to code that since I don't know where it would actually be within the object.

user and role authorization in swagger api express

guys
on my exisiting api i already have user authhication using Bearer security. Using http header api_key and later tokens.
My problem seems to be i have diffefrent end point that are only need to be consumed based on roles.
For example to post a new user :
POST user should only be authenticated to user with admin role.
I have looked at the swagger spec here but nothing i could find on thier docuemation and google as well.
Please could give me some brain stroming idea ? below is my access verifaction code in nodejs and express.
swaggerTools.initializeMiddleware(swaggerDoc, function (middleware) {
// Interpret Swagger resources and attach metadata to request - must be first in swagger-tools middleware chain
app.use(middleware.swaggerMetadata());
app.use(middleware.swaggerSecurity({
Bearer: function(req,def,apiKey,next){
apiKey= apiKey.slice(7)
debug("token check",def,apiKey)
var ok=checkToken(apiKey)
if(ok) {
req.user=ok
debug('Token is ok')
return next()
}
debug("Invalid token",apiKey)
var err=Error("Invalid token")
err.statusCode=403
next(err)
}
}));
As of this writing, the solution is still homebrewing. Swagger does not, save through oAuth scopes or using a "hacky" api-key security definition (https://stackoverflow.com/a/40222161/3736937), have a built in RBAC mechanism.
Fortunately, we can create some pretty basic middleware to handle the problem because swagger does allow us to add x-swagger-* members to the swagger definition.
So here's what I did:
Add x-swagger-roles to each endpoint that requires RBAC (Role-based Access Control)
paths:
"/":
x-swagger-router-controller: getStatus
get:
operationId: getStatus
x-swagger-roles:
- admin
tags:
- "users"
summary: "Returns message: 'working'"
description: "default endpoint for testing"
responses:
$ref: "#/definitions/AnyResponse"
Place middleware before swagger-node-runner is registered with the application. In our case we're using expressjs, so the connect middleware is used.
var findOne = function (haystack, arr) {
return arr.some(function (v) {
return haystack.indexOf(v) >= 0;
});
};
app.use(function(req, res, next) {
var operation = runner.getOperation(req);
if(operation && operation.definition) {
var definition = operation.definition;
var requiredRoles = definition['x-swagger-roles'];
// if the endpoint has no required roles then go to the next route
if(!requiredRoles) return next();
// get the users roles
var userRoles = req.session.roles; // this may differ for you
// if any roles match then go to the next route
if(findOne(userRoles, requiredRoles)) return next();
// if no roles match then assert that this endpoint is forbidden
else return res.sendStatus(403);
}
next();
})
// it's important to register the middleware after the role check
runner.expressMiddleware().register(app);
Notes:
This code has not been tested in production, and should be reviewed by a security professional.
x-swagger-roles will not appear in your swagger-ui without altering it, which is beyond the scope of this answer.

ExpressJS authorizing users with middleware?

Let's say I want to restrict the access of a resource to a certain group of people who meet some conditions. What I'm doing right now is defining an authorization middleware that checks if the req.user is meeting those conditions.
module.exports.requiresCondition = function(req, res, next){
Model.findOne({condition: condition}, function(err, model){
//check if various conditions are met. If not, return 401
res.locals.model = model;
return next();
}
}
The problem I have with this is that I can't choose what data to project because the routes that come after might use different parts of the model. This means I have to get the whole model every time, which becomes inefficient as the documents get larger and larger. Of course, I can just query once in the middleware with the keys I need to authorize and query again in the actual controller, but that doesn't seem particularly efficient either. Is there a better way to authorize users?
If different routes require different data you can modularize your middleware layer. composable-middelware https://www.npmjs.com/package/composable-middleware is perfect for that.
So let's say you have couple of routes:
import compose from 'composable-middleware';
router.get('/type1/:id', compose().use(Auth.hasAppAccess()).use(TypeMiddleware.getType1()), ctrl.doType1);
router.get('/type2/:id', compose().use(Auth.hasAppAccess()).use(TypeMiddleware.getType2()).use(TypeMiddleware.evenMoreOperations()), ctrl.doType2);
Then your middleware itself
import compose from 'composable-middleware';
export function isAuthenticated() {
return compose()
.use(function(req, res, next) {
//do your stuff here
})
}
//similarly for other middelware functions

nodejs express profile property in request

I got very confused for one usage:
In the route file:
app.param('userId', users.load);
And the users.load function:
exports.load = function (req, res, next, id) {
var options = {
criteria: { _id : id }
};
User.load(options, function (err, user) {
if (err) return next(err);
if (!user) return next(new Error('Failed to load User ' + id));
req.profile = user;
next();
});
};
Here, route should have the userId to response but why does the author use req.profile here. profile is not a property.
Anyone can help?
Thanks.
What the code does is this: for routes that have a userId parameter (that is, routes that look similar to this: /user/:userId), Express will call the load() function before the route handler is called.
The load function loads the user profile belonging to the userId from the database, and adds it to req as a newly created property req.profile.
The .profile property name is arbitrarily named by the author and demonstrates the fact that it's perfectly valid to add properties to req (or res, for that matter, but convention is to add these properties to req).
In the route handler, you can then use req.profile. It's basically a way of propagating data from middleware and app.param() implementations to other parts of the route handling.
the line req.profile = users; think of it this way, 'i want to take all the powers of the users and paste them to req.profile' why? remember this part is sort of a middleware if you want to target any of the read, update and delete code it has to pass through here, it only makes sense if it involves the req, because you are practically requesting to access the said pages (read, edit and delete or any other:userId page) now the profile name doesn't matter you could use any name but its sort of a convention in the community to use the profile name.

Resources