v0.10 Release

We're excited to announce the v0.10.0 release of OPA. This release contains more than 60 commits from 8 authors across 5 organizations. For a detailed list of changes see the GitHub releases page.
WebAssembly
This release adds experimental support for compiling OPA policies into WebAssembly (Wasm) binaries that can be executed in any Wasm runtime (e.g., V8).
The compiler is designed to be lightweight and embedded inside other programs. The package does not depend on any third-party compiler toolchains like LLVM or Wasm-specific toolchains like Emscripten. The compiled policies are fairly small in size (10KB for toy examples) and have no system call dependencies — making them easy to instantiate inside your app, serverless function, etc.
We are excited about the potential that Wasm brings to the policy enforcement space because it provides a portable, secure, and efficient runtime for answering policy queries. You can find out more about Wasm at https://webassembly.org/.
If you are feeling adventurous, you can try out the Wasm compiler today with the new opa build command. We only support a limited subset of the language today but we plan to extend coverage over the next few months. If you run into any problems, please file an issue on GitHub.
Improved Test Support with Data Mocking
This release adds support for replacing (or mocking) values under the data document using the with keyword. You can use the with keyword to replace both external JSON loaded into OPA as well as JSON generated by rules.
Prior to v0.10, OPA only allowed you to replace values under the input document. This made it hard to test contextual policies using OPA's test framework. For example, the following policy depends on "roles" data being loaded into OPA:
package authz
import data.roles
default allow = false
allow {
input.method == "GET"
input.subject.role == roles[_]
}
In v0.10.0, you can write a test rule that mocks the value of data.roles using the with keyword:
# Define some dummy inputs.
dev_input = {"method": "GET", "subject": {"role": "dev"}}
hr_input = {"method": "GET", "subject": {"role": "hr"}}
# Define some dummy role data.
fake_roles = ["hr"]
# Test the allow rule.
test_allow {
allow with input as hr_input with data.roles as fake_roles
not allow with input as dev_input with data.roles as fake_roles
}
Partial Evaluation: Negation Optimization
Earlier this year we added a feature called Partial Evaluation that helps pre-compute portions of your policy. Today, a large fragment of the language is covered by Partial Evaluation but some constructs are not fully supported.
Prior to v0.10, OPA would generate support rules for negated expressions (which can be difficult to post-process.) This was required because Rego queries only consist of a series of expressions AND-ed together. If you need to express an OR condition, you need multiple queries. When you negate an expression (e.g., not deny), the in-lined result may be a series of expressions OR-ed together. For example, given the following policy that says (in English) "no one is allowed to buy more bitcoin or eat pizza":
allow {
not deny
}
deny {
input.action = "buy"
input.resource = "bitcoin"
}
deny {
input.action = "eat"
input.resource = "pizza"
}
We can partially evaluate the deny rule to yield the following simplified queries:
+---------+----------------------------+
| Query 1 | input.action = "buy" |
| | input.resource = "bitcoin" |
+---------+----------------------------+
| Query 2 | input.action = "eat" |
| | input.resource = "pizza" |
+---------+----------------------------+
However, it's a bit trickier to partially evaluate the allow rule. OPA does not allow you to negate multiple expressions at once (you have to factor those expressions into a separate rule). In some cases though, it's reasonable to in-line the result of partially evaluating a negated expression by computing the cross-product. In this case the answer is:
+---------+--------------------------------+
| Query 1 | not input.action = "buy" |
| | not input.action = "eat" |
+---------+--------------------------------+
| Query 2 | not input.action = "buy" |
| | not input.resource = "pizza" |
+---------+--------------------------------+
| Query 3 | not input.resource = "bitcoin" |
| | not input.action = "eat" |
+---------+--------------------------------+
| Query 4 | not input.resource = "bitcoin" |
| | not input.resource = "pizza" |
+---------+--------------------------------+
By in-lining negated expressions like this, we avoid the need for support rules (which are more difficult to optimize and translate into other languages like SQL and Elasticsearch.) Of course, the size of the cross-product can get quite big, so we put a cap on what OPA will in-line.
More Great Contributions
This release also included a many other contributions from members of the community. Here are some highlights:
- JWT decode & verify built-in function. This helps implement best practices around checking the aud, exp, and nbf claims automatically.
- Client certificate support for service authentication. This allows services to authenticate OPA bundle download, status report, and decision log upload requests using client-side certificates (which may be preferred to bearer tokens.)
- Trace output in test failures. This helps policy authors debug test failures faster by pinpointing the source of the issue.
- …and many more!
