Policies
Policies are functions that execute specific logic on each request before it reaches the controller. They are mostly used for securing business logic.
Each route of a Strapi project can be associated to an array of policies. For example, a policy named is-admin
could check that the request is sent by an admin user, and restrict access to critical routes.
Policies can be global or scoped. Global policies can be associated to any route in the project. Scoped policies only apply to a specific API or plugin.
Implementation
A new policy can be implemented:
- with the interactive CLI command
strapi generate
- or manually by creating a JavaScript file in the appropriate folder (see project structure):
./src/policies/
for global policies./src/api/[api-name]/policies/
for API policies./src/plugins/[plugin-name]/policies/
for plugin policies
Global policy implementation example:
- JavaScript
- TypeScript
module.exports = (policyContext, config, { strapi }) => {
if (policyContext.state.user) { // if a session is open
// go to next policy or reach the controller's action
return true;
}
return false; // If you return nothing, Strapi considers you didn't want to block the request and will let it pass
};
export default (policyContext, config, { strapi }) => {
if (policyContext.state.user) { // if a session is open
// go to next policy or reach the controller's action
return true;
}
return false; // If you return nothing, Strapi considers you didn't want to block the request and will let it pass
};
policyContext
is a wrapper around the controller context. It adds some logic that can be useful to implement a policy for both REST and GraphQL.
To see a possible advanced usage for route policies, read the policies page of the backend customization examples cookbook.
Policies can be configured using a config
object:
- JavaScript
- TypeScript
module.exports = (policyContext, config, { strapi }) => {
if (policyContext.state.user.role.code === config.role) { // if user's role is the same as the one described in configuration
return true;
}
return false; // If you return nothing, Strapi considers you didn't want to block the request and will let it pass
};
export default (policyContext, config, { strapi }) => {
if (policyContext.state.user.role.code === config.role) { // if user's role is the same as the one described in configuration
return true;
}
return false; // If you return nothing, Strapi considers you didn't want to block the request and will let it pass
};
Usage
To apply policies to a route, add them to its configuration object (see routes documentation).
Policies are called different ways depending on their scope:
- use
global::policy-name
for global policies - use
api::api-name.policy-name
for API policies - use
plugin::plugin-name.policy-name
for plugin policies
To list all the available policies, run yarn strapi policies:list
.
Global policies
Global policies can be associated to any route in a project.
- JavaScript
- TypeScript
module.exports = {
routes: [
{
method: 'GET',
path: '/restaurants',
handler: 'Restaurant.find',
config: {
/**
Before executing the find action in the Restaurant.js controller,
we call the global 'is-authenticated' policy,
found at ./src/policies/is-authenticated.js.
*/
policies: ['global::is-authenticated']
}
}
]
}
export default {
routes: [
{
method: 'GET',
path: '/restaurants',
handler: 'Restaurant.find',
config: {
/**
Before executing the find action in the Restaurant.js controller,
we call the global 'is-authenticated' policy,
found at ./src/policies/is-authenticated.js.
*/
policies: ['global::is-authenticated']
}
}
]
}
Plugin policies
Plugins can add and expose policies to an application. For example, the Users & Permissions plugin comes with policies to ensure that the user is authenticated or has the rights to perform an action:
- JavaScript
- TypeScript
module.exports = {
routes: [
{
method: 'GET',
path: '/restaurants',
handler: 'Restaurant.find',
config: {
/**
The `isAuthenticated` policy prodived with the `users-permissions` plugin
is executed before the `find` action in the `Restaurant.js` controller.
*/
policies: ['plugin::users-permissions.isAuthenticated']
}
}
]
}
export default {
routes: [
{
method: 'GET',
path: '/restaurants',
handler: 'Restaurant.find',
config: {
/**
The `isAuthenticated` policy prodived with the `users-permissions` plugin
is executed before the `find` action in the `Restaurant.js` controller.
*/
policies: ['plugin::users-permissions.isAuthenticated']
}
}
]
}
API policies
API policies are associated to the routes defined in the API where they have been declared.
- JavaScript
- TypeScript
module.exports = async (policyContext, config, { strapi }) => {
if (policyContext.state.user.role.name === 'Administrator') {
// Go to next policy or will reach the controller's action.
return true;
}
return false;
};
module.exports = {
routes: [
{
method: 'GET',
path: '/restaurants',
handler: 'Restaurant.find',
config: {
/**
The `is-admin` policy found at `./src/api/restaurant/policies/is-admin.js`
is executed before the `find` action in the `Restaurant.js` controller.
*/
policies: ['is-admin']
}
}
]
}
export default (policyContext, config, { strapi }) => {
if (policyContext.state.user.role.name === 'Administrator') {
// Go to next policy or will reach the controller's action.
return true;
}
return false;
};
export default {
routes: [
{
method: 'GET',
path: '/restaurants',
handler: 'Restaurant.find',
config: {
/**
The `is-admin` policy found at `./src/api/restaurant/policies/is-admin.js`
is executed before the `find` action in the `Restaurant.ts` controller.
*/
policies: ['is-admin']
}
}
]
}
To use a policy in another API, reference it with the following syntax: api::[apiName].[policyName]
:
- JavaScript
- TypeScript
module.exports = {
routes: [
{
method: 'GET',
path: '/categories',
handler: 'Category.find',
config: {
/**
The `is-admin` policy found at `./src/api/restaurant/policies/is-admin.js`
is executed before the `find` action in the `Restaurant.js` controller.
*/
policies: ['api::restaurant.is-admin']
}
}
]
}
export default {
routes: [
{
method: 'GET',
path: '/categories',
handler: 'Category.find',
config: {
/**
The `is-admin` policy found at `./src/api/restaurant/policies/is-admin.ts`
is executed before the `find` action in the `Restaurant.js` controller.
*/
policies: ['api::restaurant.is-admin']
}
}
]
}