Secure Java REST APIs With JSON XACML and ALFA
In this post, we’ll look at how to use a JSON XACML Policy Enforcement Point to secure REST API endpoints. We will use ALFA, XACML, and JSON to do so.
Setting the Scene
Let’s imagine we have a REST API that helps us manage purchase orders. The API provides users with the ability to view, edit, approve, and delete purchase orders.
Business Data
In this simple example, we will have data about employees and data about purchase orders. The simplified ER diagram looks like the following.
The API
We will expose the following functionality via the API.
HTTP Verb |
Entire Collection (/purchaseorders) |
Specific purchase order (e.g. /purchaseorders/{id}) |
POST |
201 (Created), 'Location' header with link to /purchaseorders/{id} containing new ID. |
404 (Not Found), 409 (Conflict) if resource already exists.. |
GET |
200 (OK), list of purchase orders. |
200 (OK), single customer. 404 (Not Found), if ID not found or invalid. |
PUT |
405 (Method Not Allowed). |
200 (OK) or 204 (No Content). 404 (Not Found), if ID not found or invalid. |
DELETE |
405 (Method Not Allowed) |
200 (OK). 404 (Not Found), if ID not found or invalid. |
The API can be implemented using any language you like, e.g. .NET, Java, or other.
Defining Authorization Requirements
Given the limited set of data in this sample use case, here are some authorization requirement examples:
- A manager in the purchasing department can create a new purchase order
- A manager in the finance department can view purchase orders in their regions.
- A manager in the finance department can approve a purchase order in their region if the PO amount is less than the user’s approval amount.
- No one can view the credit card number associated with a purchase order.
Authorization requirements are defined separately based on the business analysts’ user stories. They are decoupled from the application/REST API so that:
- They can evolve independently of the API codebase.
- They can be expressed as policies and provide more transparency into the authorization.
Implementing the Requirements Using ALFA
ALFA is a lightweight syntax that lets you write authorization policies extremely easily, reducing the friction of writing policies in XACML, and also reaping the benefits of using this standardized language, all while expressing relationships and graphs. Axiomatics donated the syntax definition to OASIS as a candidate profile. Axiomatics also provides, free of charge for non-production usage, an Eclipse plugin to edit policies written in ALFA as well as a command-line tool to compile ALFA into standard XACML.
namespace axiomatics {
/**
* No one can view the credit card number
* associated with a purchase order.
*
*/
policyset purchaseorder {
target clause objectType == "purchase order"
apply firstApplicable
/**
* View...
*/
policyset viewPO {
target clause actionId == "view"
apply firstApplicable
policy managers {
target clause role == "manager"
and department == "finance"
apply firstApplicable
/**
* A manager in the finance department
* can view purchase orders in their regions
*/
rule allowSameRegion {
permit
condition user.region == po.region
}
}
}
/**
* Create...
*/
policy createPO {
target clause actionId == "create"
apply firstApplicable
/**
* A manager in the purchasing department
* can create a new purchase order.
*/
rule managersCanCreate {
target clause role == "manager"
and department == "purchasing"
permit
}
}
/**
* Approve...
*/
policyset approvePO {
target clause actionId == "approve"
apply firstApplicable
/**
* Managers in finance
*/
policy managers {
target clause role == "manager"
and department == "finance"
apply firstApplicable
/**
* A manager in the finance department
* can approve a purchase order in their region
* if the PO amount is less than the approval amount.
*/
rule approveUptoLimit {
permit
condition po.amount <= user.approvalLimit
}
}
}
}
}
Defining a REST Policy Enforcement Point (PEP)
Let’s assume we are implementing the REST API using Java and JAX-RS. The same principle will apply to other technologies.
Intercepting at the Right Layer
We need to implement a PEP that is capable of inspecting the request (and optionally inspecting or manipulating the response). Given JAX-RS services build on top of Java Servlets, we could write a Servlet filter. However, such a filter would not give us access to the structured information. Rather, JAX-RS comes with interceptors, which is a type of plug-in that gives a developer access to a structured message body (JSON or XML) as it is being read or written.
There are two kinds of interceptors, ReaderInterceptor and WriterInterceptor. Reader interceptors are applied to inbound entity streams. A reader interceptor lets you manipulate the request entity stream. Writer interceptors let you manipulate the response e.g. to redact or mask data. For instance, the fourth requirement (No one can view the credit card number associated with a purchase order) would be implemented by altering the response.
Sending the Right Authorization Request
Use the ReaderInterceptor to parse the incoming message and generate the relevant authorization request. Think about elevating the request i.e. crafting an authorization request that is meaningful from a business point-of-view rather than a request that is technical. For instance use view, edit, delete rather than POST or GET.
Use the JSON Profile of XACML to generate your XACML request as it is more lightweight and simpler than XML.
Enforcing on the Way In and Out
As previously mentioned, use the WriterInterceptor to manipulate the response and redact part of the response.
Tying it All Together
Once your enforcement point is written, you can deploy it to your REST API and start applying fine-grained, relationship-based authorization. Do not forget to deploy your authorization policies.