Apply multiple policies in sails on a route - node.js

How to apply multiple policies in sails on routes which are generated by sails like this : /users/:id/orders . I can apply a single policy like this in config/routes.js
'/users/:id/orders' : {
policy : 'isAuthenticated'
}
But how can apply more than one policy in similar manner

Sadly the documentation http://sailsjs.org/documentation/concepts/routes/custom-routes#?policy-target-syntax does not talk about chaining policies in routes.
As an alternative your could protect the populate action in your user controller like so: edit config/policies.js
UserController: {
populate: ['isAuthenticated', 'isAllowed']
}
http://sailsjs.org/documentation/reference/blueprint-api/populate-where
If you just want to apply the policy only to the orders association, you can retrieve the association parameter (/:model/:id/:association) from the req object inside the policy and handle your case:
module.exports = function(req, res, next) {
if (req.param('association') == 'orders') {
// do your magic
} else {
return next();
}
};

Related

Loopback before save update all instance

Hey all quick question about before save on UpdateAll
Hypothetically I have a customer model, that hasMany Orders. In an operation hook monitoring before save of the Customer I'm trying to create an order automatically. The trick is that I'm trying to do this when doing customers UpdateAll
Based on the docs I see there's no way to get the id of the current instance of the customer
But is there a way to do something like ctx.instance.orders.create(data); ?
I believe in case like this, using persist hook will make more sense. You can access the id via ctx.currentInstance.id. This hook is triggered by operations that persist data to the datasource, including update.
MyModel.observe('persist', function(ctx, next) {
if (ctx && ctx.currentInstance.id) {
// create order here
}
next();
});
updateAll is an alias function of update.
You can try something like,
Customer.observe('before save', (ctx, next) => {
if (ctx.instance && ctx.instance.id ) {
// create order here
}
next();
});
ctx.instance.id is avaialble only when you pass it.
i.e
Customer.update({id: 1}, {name: 'New Name'}) -in this case ctx.instance.id will be available.
Customer.update({name: 'New Name'}) - in this case ctx.instance.id won't be available. Also this will update name of all the customer.

SailsJS. Generic policy which check if groupID of element in request is the same as user's group

I have big API and I'm using blueprints as it is really convenient for most of operations.
It is multi-tenant application, so every element has groupID field.
I need to have policy which will check if element which is going to be edited/deleted belongs to user's group.
I have user's groupID in request already injected by JWT policy,
but how can I make policy to generic policy which will check appropriate model for id and compare it with user id ?
It can't be done by groupID from request as hacker can use his JWT token and put his groupID in request but access not his element.
IS it possible or I have to create separate policy per model?
I found it.
You can get modelName from req.options.model when you are using Blueprints.
Unfortunately you can't use this[modelName] as option is giving you model name starting with small letter, so first you have to upper case first letter with e.g. var modelName = req.options.model.charAt(0).toUpperCase() + req.options.model.slice(1);
and then you are free to use this[modelName].whateverYouNeed
I used it for generic policy to let user editing only his own group elements.
var modelName = req.options.model.charAt(0).toUpperCase() + req.options.model.slice(1)
var elementID = null
if (req.params.id) { // To handle DELETE, PUT
elementID = req.params.id
}
if (req.body.id) { // To handle POST
elementID = req.body.id
}
this[modelName].findOne({
id: elementID
}).exec(function(err, contextElement) {
if(err) {
return res.serverError(err)
}
if(contextElement.group=== req.user.group.id) {
sails.log('accessing own: ' + modelName)
return next()
}
else {
return res.forbidden('Tried to access not owned object')
}
})

Disable blueprint routes sails js

Hello I have model users, in which there is a foreign key of Orders model.
Now sails will automatically generate route /users/:id/orders. I have to disable this route. How to do this ? I have already tried to disable all routes of orders using: _config : { actions: false, rest: false, shortcuts: false } but it still doen't work
You can achieve this by adding custom routes, which will overwrite the blueprint action.
Use http://sailsjs.org/documentation/concepts/routes/custom-routes#?response-target-syntax
'/users/:id/orders': {response: 'forbidden'}
or http://sailsjs.org/documentation/concepts/routes/custom-routes#?function-target-syntax
'/users/:id/orders': function(req, res) {res.forbidden();}
You could control the access to this model through policies.
For blocking everything put the code below inside your /config/policies.js file:
Orders : {
'*': false
},
You could also overwrite the route
In /config/routes.js:
'/:collection/:id/:model': {response: 'forbidden'}
Or you could do the way you've done, disabling the rest routes on this model
Just make sure you put the whole block, including the export line:
module.exports = {
_config: {
rest: false
}
};

Add and Remove Within Mongoose

{
"_id" : 1,
"_name" : "peter",
"favourites" : [
{
"user_id" : 1
},
{
"user_id" : 2
}
]
}
I have this schema within Mongo, and I'm using Node/Express/Mongoose to manage my stack. I'm new to this particular stack and I have a 'favourite' button on the website I'm building. What's the best way to add/remove from favourites?
Do I really need to setup two routes, one for POST and another for PUT? I'm not sure what the best way is to solve this particular problem, I've done some searching and I've not found anything that relevant.
I'm not going to give you the full answer as it's always good to learn, so I've give you 90% of what I think is what you're looking for. Do a check for the name and if it doesn't exist within the favourites array, do the following:
db.collection.update(
{ "username": "walterwhite" },
{ $push:
{
favourites: [ { "user_id": "7" } ]
}
}
)
First of all you will need routes for any of the actions. It's probably the best practice to use the HTTP Verbs (GET/POST/PUT/DELETE etc.). Then your routes would look something like this: (this could be wrapped in a file that contains all routes and later required in the main app file (ex. app.js).)
module.exports = function (router) {
router.route('/favourites')
.get(function (req, res, next) {
// Code for getting all items
})
.post(function (req, res, next) {
// Code for inserting new item
})
.delete(function (req, res, next) {
// Code for deleting an item
});
}
Then you will need to build a form with fields corresponding to the item's properties and use POST as a submit method. You will need it for inserting (adding) new items. The request sent from the form, will send you to the post route, where you will handle your logic for appending item to the list.
To implement the deletion you will have to send a DELETE, request passing an identifier of the item you'd want to delete. You will handle this code in the delete route.
Of course, the get route is for getting all items in the collection for further processing or visualizing.
you must create an object id associated to every user_id. you can create such id's using.you can associate multiple models with mongoose population
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
"favourites" : [
{
"_id" : ObjectId("57338c9cc5c8cf74181b4cff"),
"user_id" : 1
},
{
"_id" : ObjectId("5734588e5a54d45434693a09")
"user_id" : 2
}
]
either render the page or send a json object as response with the object id in it as per your setup.
user_id-2
user_id-1
now you can make an ajax request with the object id when clicked on a particular <a> tag.you can achieve this with jquery. Then remove the user associated with that id from your favorites.
If you add more details.I can give you a specific answer.

override sails GET blueprint with many-to-one

caveat: new to web programming. I have the following in a sails project:
// in models/User.js
// user has many videos
module.exports = {
attributes: {
videos: {
collection: 'video',
via: 'owner'
}
}
}
// in models/Video.js
// video has one user
module.exports = {
attributes: {
owner: {
model: 'user'
}
}
}
I'm using the REST blueprints with a /v1 prefix. I'm trying to either override GET /v1/user/:id/video, or make a custom route to GET /v1/user/:id/myGetVideo
So I've tried the following:
find and findOne methods in VideoController.js and UserController.js. These don't trigger for GET /v1/user/:id/video - I guess because the many-to-one association is treated differently
creating a custom route
// in routes.js
module.exports.routes = {
'get /user/:id/myGetVideo': 'UserController.myGetVideo'
}
This doesn't trigger anything because of the namespace, but it does trigger /user/:id/myGetVideo as expected. I don't want to add /v1 namespace here b/c that will proliferate and become an issue to change versions.
So, how do I either override the method I want, or put it in the namespace? Overriding the first method seems cleaner, but idk.
Thanks for any help!
Edit: Any comments on what is better practice for a REST API?
Got it with wildcards:
// in routes.js
module.exports.routes = { 'get /v*/user/:id/myGetVideo':'UserController.myGetVideo' }
not bullet-proof if the prefix scheme changes, but good enough

Resources