Policy Framework
Learn how to authorize access to routes
Introduction
Policies are application entities that authorize a request. A policy is called after a request is validated and sanitized and before the request is executed on the target action. When a policy fails, the default status code is 403. Examples of policies can include
Verifying the value of the HTTP authorization header
Checking for violations of rate limits
Enacting a pay wall
All policies are located in app/policies
.
Implementing a Policy
You implement a policy by extending the Policy
class, and implementing the runCheck(req)
method. The runCheck(req)
method must return true
if the policy passes. If the policy fails, then the runCheck(req)
method must return false
, or an the object {failureCode, failureMessage}
.
If the policy check has an asynchronous operation, such as querying a database, then the runCheck(req)
method can return a Promise
. The Promise can resolve with true
, false
, or the object {failureCode, failureMessage}
.
Default Failure Code and Message
The failureCode
is an application-specific code used to identify the reason for failure. The failureMessage
is a human readable message that can be displayed to the user. When apply fails, you have the option of returning the object {failureCode, failureMessage}
. You also have the option of returning false
. When you return false, there is specification of failureCode
and failureMessage
. This is where the default failureCode
and failureMessage
for the policy come into play.
The default failureCode
and failureMessage
is used when the runCheck(req)
method returns false
.
The default failureCode
and failureMessage
are just properties on the Policy
. Here we have updates the passthrough
policy from above with a default failureCode
and failureMessage
.
Now, when the runCheck(req)
method returns false, it will send the following response:
Setting Parameters
Policies can also take parameters, which can be used to configure dynamic behavior in a policy. For example, what if we want the passthrough policy to be configured with the result of true
or false
. This means we need a way to configure the policy with the expected result. We do this by via policy parameters.
To support parameters, implement the setParameters()
method on the policy. Each argument in setParameters()
is an individual parameter. Below, we have updated our simple passthrough policy to use a parameter to define the result of the policy.
As illustrated above, the setParameters(value)
method stores the parameter value. Likewise, the runCheck(req)
method uses the parameter value as its result.
Applying Policies to Routes
Now that we have defined our passthrough
policy, our next step is to apply the policy to different routes. We apply a policy to a route by naming the policy using the policy
property in the router specification.
Use dot notation to access policies located in subdirectories. For example, the policy a/b/c
can accessed using the name a.b.c
.
In this example, we are applying the passthrough polices to all routes under the /messages
path.
Now, anytime we the client sends a request to /messages
on the application server, the passthrough policy will authorize the request.
Passing Parameters
The default behavior of the passthrough
policy is allow the authorization to succeed. This is because the default value of the value
property is true. But, what if we want the authorization to fail. The passthrough
policy supports parameters, but we need to pass false
to as a parameter value to the policy.
If you need to pass parameters to a policy, then you must use the check(name, ...args)
method. The first parameter to the check()
method is the name of the policy. The remaining arguments are the parameters to the policy in-order of their argument specification in setParameters()
.
We have now updated the example so that the create route will experience a policy failure.
Optional Policies
An optional policy is a policy that is applied if it exists. This is useful when you are defining a router in a Blueprint module, and want to give the module user the option of applying a policy to a route. To declare a policy on a route optional, begin the name with a question mark (?
). For example, the create action now has an optional policy.
Negating Policies
Similar to optional policies, you can also negate a policy. For example, if the policy returns true, then the negated policy will return false. If the negated policy returns false
or {failureCode, failureMessage}
, then it will return true
. To negate a policy, begin the policy name with a exclamation point (!
). For example, the create action now has a negated policy.
Aggregate Policies
An aggregate policy is a policy created by combining one or more policies. There are two common use cases supported by default. Either all the policies succeed or any of the policies succeed for the aggregate policy to succeed.
all
Use the all()
method to create an aggregate policy where all policies must succeed in order for the aggregate policy to succeed. The all()
method takes a list of policies, and an optional failureCode
and failureMessage
parameter.
Ordered execution
Use all.ordered()
to evaluate the aggregates policies in order instead of in parallel.
any
Use the any()
method to create an aggregate policy where at least one policies must succeed in order for the aggregate policy to succeed. The any()
method takes a list of policies, and an optional failureCode
and failureMessage
parameter.
Ordered execution
Use any.ordered()
to evaluate the aggregates policies in order instead of in parallel.
Last updated