Example 1: Basic ABAC with OPA and Rego - AWS Prescriptive Guidance

Example 1: Basic ABAC with OPA and Rego

This section describes a scenario where OPA is used to make access decisions about which users are allowed to access information in a fictional Payroll microservice. Rego code snippets are provided to demonstrate how you can use Rego to render access control decisions. These examples are neither exhaustive nor a full exploration of Rego and OPA capabilities. For a more thorough overview of Rego, we recommend that you consult the Rego documentation on the OPA website.

Basic ABAC with OPA and Rego

Basic OPA rules example

In the previous diagram, one of the access control rules enforced by OPA for the Payroll microservice is:

Employees can read their own salary.

If Bob tries to access the Payroll microservice to see his own salary, the Payroll microservice can redirect the API call to the OPA RESTful API to make an access decision. The Payroll service queries OPA for a decision with the following JSON input:

{ "user": "bob", "method": "GET", "path": ["getSalary", "bob"] }

OPA selects a policy or policies based on the query. In this case, the following policy, which is written in Rego, evaluates the JSON input.

default allow = false allow = true { input.method == "GET" input.path = ["getSalary", user] input.user == user }

This policy denies access by default. It then evaluates the input in the query by binding it to the global variable input. The dot operator is used with this variable to access the variable's values. The Rego rule allow returns true if the expressions in the rule are also true. The Rego rule verifies that the method in the input is equal to GET. It then verifies that the first element in the list path is getSalary before assigning the second element in the list to the variable user. Lastly, it checks that the path being accessed is /getSalary/bob by checking that the user making the request, input.user, matches the user variable. The rule allow applies if-then logic to return a Boolean value, as shown in the output:

{ "allow": true }

Partial rule using external data

To demonstrate additional OPA capabilities, you can add requirements to the access rule you are enforcing. Let's assume that you want to enforce this access control requirement in the context of the previous illustration: 

Employees can read the salary of anyone who reports to them.

In this example, OPA has access to external data that can be imported to help make an access decision:

"managers": { "bob": ["dave", "john"], "carol": ["alice"] }

You can generate an arbitrary JSON response by creating a partial rule in OPA, which returns a set of values instead of a fixed response. This is an example of a partial rule:

direct_report[user_ids] { user_ids = data.managers[input.user][_] }

This rule returns a set of all users that report to the value of input.user, which, in this case, is bob. The [_] construct in the rule is used to iterate over the values of the set. This is the output of the rule:

{ "direct_report": [ "dave", "john" ] }

Retrieving this information can help determine whether a user is a direct report of a manager. For some applications, returning dynamic JSON is preferable to returning a simple Boolean response.

Putting it all together

The last access requirement is more complex than the first two because it combines the conditions specified in both requirements:

Employees can read their own salary and the salary of anyone who reports to them.

To fulfill this requirement, you can use this Rego policy:

default allow = false allow = true { input.method == "GET" input.path = ["getSalary", user] input.user == user } allow = true { input.method == "GET" input.path = ["getSalary", user] managers := data.managers[input.user][_] contains(managers, user) }

The first rule in the policy allows access for any user who tries to see their own salary information, as discussed previously. Having two rules with the same name, allow, functions as a logical or operator in Rego. The second rule retrieves the list of all direct reports associated with input.user (from the data in the previous diagram) and assigns this list to the managers variable. Lastly, the rule checks whether the user who is trying to see their salary is a direct report of input.user by verifying that their name is contained in the managers variable.

The examples in this section are very basic and do not provide a complete or thorough exploration of the capabilities of Rego and OPA. For more information, review the OPA documentation, see the OPA GitHub README file, and experiment in the Rego playground.