Edit Page

Policies

Overview

#

Policies in Sails are versatile tools for authorization and access control: they let you execute some logic before an action is run in order to determine whether or not to continue processing the request. The most common use-case for policies is to restrict certain actions to logged-in users only.

NOTE: policies apply only to controllers and actions, not to views. If you define a route in your routes.js config file that points directly to a view, no policies will be applied to it. To make sure policies are applied, you can instead define an action which displays your view and then point your route to that action.  

When to use policies

#

It's best to avoid implementing numerous or complex policies in your app. Instead, when implementing features like granular, role-based permissions, rely on your actions to reject unwanted access. Your actions should also be responsible for any necessary personalization of the view locals and JSON response data you send in the response.

For example, if you need to implement user-level or role-based permissions in your application, the most straightforward solution is to take care of the relevant checks at the top of your controller action—either inline or by calling out to a helper. Following this best practice will significantly improve the maintainability of your code.

Protecting actions and controllers with policies

#

Sails has a built in ACL (access control list) located in config/policies.js. This file is used to map policies to actions and controllers.

This file is declarative, meaning it describes what the permissions for your app should look like rather than how they should work. This makes it easier for new developers to understand what's going on, and it makes your app more flexible as requirements inevitably change over time.

The config/policies.js file is a dictionary whose properties and values differ depending on whether you are applying policies to controllers or standalone actions.

Applying policies to a controller
#

To apply policies to a controller, use the controller name as the name of a property in the config/policies.js dictionary, and set its value to a dictionary mapping actions in that controller to policies that should be applied to them. Use * to represent “all unmapped actions”. A policy's name is the same as its filename, minus the file extension.

module.exports.policies = {
  UserController: {
    // By default, require requests to come from a logged-in user
    // (runs the policy in api/policies/isLoggedIn.js)
    '*': 'isLoggedIn',

    // Only allow admin users to delete other users
    // (runs the policy in api/policies/isAdmin.js)
    'delete': 'isAdmin',

    // Allow anyone to access the login action, even if they're not logged in.
    'login': true
  }
};
Applying policies to standalone actions
#

To apply policies to one or more standalone actions, use the action path (relative to api/controllers) as a property name in the config/policies.js dictionary, and set the value to the policy or policies that should apply to those actions. By using a wildcard * at the end of the action path, you can apply policies to all actions that begin with that path. Here's the same set of policies as above, rewritten to apply to standalone actions:

module.exports.policies = {
  'user/*': 'isLoggedIn',
  'user/delete': 'isAdmin',
  'user/login': true
}

Note that this example differs slightly from that of the controller-based policies in that the isLoggedIn policy will apply to all actions in the api/controllers/user folder and subfolders (except for user/delete and user/login, as is explained in the next section).

Policy ordering and precedence
#

It is important to note that policies do not cascade. In the examples above, the isLoggedIn policy will be applied to all actions in the UserController.js file (or standalone actions living under api/controllers/user ) except for delete and login. If you wish to apply multiple policies to an action, list the policies in an array. For example:

'getEncryptedData': ['isLoggedIn', 'isInValidRegion']
Using policies with blueprint actions
#

Sails' built-in blueprint API is implemented using regular Sails actions. The only difference is that blueprint actions are implicit.

To apply your policies to blueprint actions, set up your policy mappings just like we did in the example above, but pointed at the name of the relevant implicit blueprint action in your controller (or as a standalone action). For example:

module.exports.policies = {
  UserController: {
    // Apply the 'isLoggedIn' policy to the 'update' action of 'UserController'
    update: 'isLoggedIn'
  }
};

or

module.exports.policies = {
  'user/update': 'isLoggedIn'
};
Global policies
#

You can apply a policy to all actions that are not otherwise explicitly mapped by using the * property. For example:

module.exports.policies = {
  '*': 'isLoggedIn',
  'user/login': true
};

This would apply the isLoggedIn policy to every action except the login action in api/controllers/user/login.js (or in api/controllers/UserController.js).

Built-in policies

#

Sails provides two built-in policies that can be applied globally or to a specific controller or action:

Writing Your First Policy

#

Here is a simple isLoggedIn policy to prevent access for unauthenticated users. It checks the session for a userId property, and if it doesn’t find one, sends the default forbidden response value.

// policies/isLoggedIn.js
module.exports = async function (req, res, proceed) {

  // If `req.me` is set, then we know that this request originated
  // from a logged-in user.  So we can safely proceed to the next policy--
  // or, if this is the last policy, the relevant action.
  // > For more about where `req.me` comes from, check out this app's
  // > custom hook (`api/hooks/custom/index.js`).
  if (req.me) {
    return proceed();
  }

  //--•
  // Otherwise, this request did not come from a logged-in user.
  return res.forbidden();

};

Next up: Helpers

Is something missing?

If you notice something we've missed or could be improved on, please follow this link and submit a pull request to the sails repo. Once we merge it, the changes will be reflected on the website the next time it is deployed.

Concepts