Policies are additive. Anything you might do with policies, you could just implement in your custom actions directly. But they can make your life a lot easier.
Policies can be used like middleware, meaning you can do almost anything you can imagine with them. That said, our experience using Sails to build all sorts of different apps has taught us that policies are best used for one, very specific purpose: preventing access to actions for certain users (or types of users) where those actions are not accessible in the UI. That is, policies are best used like preconditions-- you can use them to take care of edge cases that are only possible by cheating the UI.
For example, imagine you're building an action called changePassword
in your UserController
. Its job is to take the new password that was provided, encrypt it, then update the database record for the currently-logged-in user to save the new encryped password. When you implement and test this action, your first inclination is to log in first. In fact, it's nonintuitive to check what happens if you're not logged in! This is a perfect example of where policies are helpful. You can implement your action to handle the cases that are actually accessible from your UI; then handle edge cases which are NOT trigger-able from the UI using policies.
In other words, instead of adding the following to the top of your action code:
// Check that the request is originating from a logged-in user before we get too deep in the weeds.
if (!req.session.myUserId) {
return res.forbidden();
}
You could implement a policy called isLoggedIn
and apply it to UserController.changePassword
in config/policies.js
. The implementation of said policy might look like:
if (req.session.myUserId) {
return next();
}
return res.forbidden();
I used to have a very different stance on policies. However, working on countless Sails apps has taught me a number of rules of the road that, if followed, help prevent your app from becoming tangled and confusing. If you have been using Sails for a while, you'll notice that some of these suggestions are an about-face from what I suggested in the past. I'm sorry- I was wrong :/ I did my best to explain the why in each case, but please let me know if I missed something.
Truly reusable policies should only look at req.session and the database- NOT at parameters! Relying on parameters makes your policy context-specific and only usable in very specialized circumstances. And in that case, why not just put the code in the relevant action(s)?
Policies should be nullipotent-- that is, they should not set, write, or frankly "do" anything. In the past, I suggested the strategy of using custom properties of req
(specifically req.options
) to allow your policies to take on more responsibilities. But in practice, I've learned that this ends up causing more pain than it alleviates.
Policies are a great way to protect against edge cases that are not accesible in the UI. But they are not a good tool for structuring logic in your app. Using them this way makes them just as versatile as raw Express/Connect middleware-- and unfortunately it also makes them just as dangerous and developer-specific. If you use policies to help with queries, or create complex chain of policies to manage permission systems in your app, you are likely to create a code base that is very difficult for any other developer to understand.
@mikermcneil
We are quite new to Sails JS.
We have a Sails app running locally inside our company’s network, which is still under development.
We made this SO post (https://stackoverflow.com/questions/49990040/sails-js-user-management-best-practice-2018) asking for the best practice for managing roles and policies, and now we just stumbled upon this FAQ.
As our knowledge of Sails policies comes from your Sails.JS In Action book, we were wondering if it still has the best examples to manage them because of what you say here.
Is the book still the best practice ?