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, 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 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 approach 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 enhance 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 your actions and controllers.

This file is declarative, meaning it describes what the permissions for your app should look like, not how they should work. This makes it easier for new developers to jump in and understand what's going on, plus it makes your app more flexible as your 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.

  • true: public access (allows anyone to get to the mapped controller/action)
  • false: NO access (allows no-one to access the mapped controller/action)

    '*': true is the default policy for all controllers and actions. In production, it's good practice to set this to false to prevent access to any logic you might have inadvertently exposed.

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. (For many apps, this will likely be the only policy needed.) The following example assumes that, in the controller actions for authenticating a user, you set req.session.userId to a truthy 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();

};

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-docs repo. Once we merge it, the changes will be reflected on the website the next time it is deployed.

Sails logo
  • Home
  • Get started
  • Support
  • Documentation
  • Documentation

For a better experience on sailsjs.com, update your browser.

Documentation

Reference Concepts App structure | Upgrading Contribution guide | Tutorials More

Concepts

  • Actions and controllers
    • Generating actions and controllers
    • Routing to actions
  • Assets
    • Default tasks
    • Disabling Grunt
    • Task automation
  • Blueprints
    • Blueprint actions
    • Blueprint routes
  • Configuration
    • The local.js file
    • Using `.sailsrc` files
  • Deployment
    • FAQ
    • Hosting
    • Scaling
  • E-commerce
  • Extending Sails
    • Adapters
      • Available adapters
      • Custom adapters
    • Custom responses
      • Adding a custom response
    • Generators
      • Available generators
      • Custom generators
    • Hooks
      • Available hooks
      • Events
      • Hook specification
        • .configure()
        • .defaults
        • .initialize()
        • .registerActions()
        • .routes
      • Installable hooks
      • Project hooks
      • Using hooks
  • File uploads
    • Uploading to GridFS
    • Uploading to S3
  • Globals
    • Disabling globals
  • Helpers
    • Example helper
  • Internationalization
    • Locales
    • Translating dynamic content
  • Logging
    • Custom log messages
  • Middleware
    • Conventional defaults
  • Models and ORM
    • Associations
      • Many-to-many
      • One way association
      • One-to-many
      • One-to-one
      • Reflexive associations
      • Through associations
    • Attributes
    • Errors
    • Lifecycle callbacks
    • Model settings
    • Models
    • Query language
    • Records
    • Standalone Waterline usage
    • Validations
  • Policies
    • Access Control and Permissions
  • Programmatic usage
    • Tips and tricks
  • Realtime
    • Multi-server environments
    • On the client
    • On the server
  • Routes
    • Custom routes
    • URL slugs
  • Security
    • Clickjacking
    • Content security policy
    • CORS
    • CSRF
    • DDOS
    • P3P
    • Socket hijacking
    • Strict Transport Security
    • XSS
  • Services
  • Sessions
  • Shell scripts
  • Testing
  • Views
    • Layouts
    • Locals
    • Partials
    • View engines

Built with Love

The Sails framework is built by a web & mobile shop in Austin, TX, with the help of our contributors. We created Sails in 2012 to assist us on Node.js projects. Naturally we open-sourced it. We hope it makes your life a little bit easier!

Sails:
  • What is Sails?
  • Community
  • News
  • For business
About:
  • Our company
  • Security
  • Legal
  • Logos/artwork
Help:
  • Get started
  • Documentation
  • Docs
  • Contribute
  • Take a class

© 2012-2018 The Sails Company. 
The Sails framework is free and open-source under the MIT License.