Data Filters Compilation API
Creating data filters from Rego policies in Enterprise OPA uses an expanded version of the Open Policy Agent /v1/compile
API1.
Generate Filters
POST /v1/compile
Content-Type: application/json
Accept: ...
Request Headers
Name | Required | Accepted Values | Description |
---|---|---|---|
Content-Type | Yes | application/json application/x-yaml | Indicates the request body is either a JSON or YAML encoded document. |
Content-Encoding | No | gzip | Indicates the request body is a compressed gzip object. |
Accept | Yes | See below | See below |
Accept Header – Controlling the Target Response Format
The same request can generate filters that are representable in many different ways, such as raw SQL WHERE
clauses or Universal Conditions AST.
Enterprise OPA uses the Accept
header to denote the target response format.
Value | Response Schema | Description |
---|---|---|
Rego AST: application/json | result.queries[] | The default behavior of Open Policy Agent. Response format is JSON in Rego AST |
Multitarget: application/vnd.styra.multitarget+json | result.{ucast,sqlserver,mysql,postgresql} | The partially evaluated result of the query in each target dialect. Use the options.targetDialects field in the request body to control targets. |
UCAST: application/vnd.styra.ucast.all+json , application/vnd.styra.ucast.minimal+json , application/vnd.styra.ucast.linq+json , application/vnd.styra.ucast.prisma+json | result.query | UCAST JSON object describing the conditions under which the query is true. |
SQL: application/vnd.styra.sql.sqlserver+json , application/vnd.styra.sql.mysql+json , application/vnd.styra.sql.postgresql+json | result.query | String representing the SQL equivalent of the conditions under which the query is true. |
Request Body
Field | Type | Required | Description |
---|---|---|---|
query | string | Yes | The query to partially evaluate and compile. e.g., data.filters.include |
input | any | No | The input document to use during partial evaluation (default: undefined). |
options | object[string, any] | No | Additional options to use during partial evaluation |
options.disableInlining | array[string] | No. Default: undefined | A list of rule references |
options.nondeterminsticBuiltins | bool | No. Default: false . If using any target other than Rego AST, it is always true | When false , non-deterministic built-in functions are kept in the PE results, without being partially evaluated. |
options.targetDialects | array[string] , one of ucast+all , ucast+minimal , ucast+prisma , ucast+linq , sql+sqlserver , sql+mysql , sql+postgresql | Yes, if using multitarget . Ignored for all other targets | The output targets for partial evaluation. Different targets will have different constraints. Use Accept header to request a single compilation target. |
options.targetSQLTableMappings | object[string, object[string, string]] | No | A mapping between tables and columns. See the example for the schema. |
unknowns | array[string] | No | The terms to treat as unknown during partial evaluation (default: ["input"] ]). |
Query Parameters
See the Open Policy Agent /v1/compile
API1
SQL Examples
In this support ticket application example, the list of tickets a user can see is determined by their role.
Roles are provided to Enterprise OPA via roles/data.json
, and the SQL tables contain tickets and assignees.
- filters.rego
- roles/data.json
package filters
import rego.v1
# Include in results if
# User has the "admin" role.
include if {
"admin" in data.roles[input.user]
}
# Include in results if
# User has the "reader" role, and the ticket is assigned to them.
include if {
"reader" in data.roles[input.user]
# Name field on Users table == the current user.
# input.<table>.<column>
input.users.name == input.user
}
# Include in results if
# User has the "resolver" role, and the ticket is assigned to them.
include if {
"resolver" in data.roles[input.user]
# ticket is assigned to user
input.users.name == input.user
}
# Include in results if
# User has the "resolver" role, and the ticket is unassigned and unresolved.
include if {
"resolver" in data.roles[input.user]
# ticket is unassigned and unresolved
input.tickets.assignee == null
input.tickets.resolved == false
}
{
"alice": ["admin"],
"bob": ["reader"],
"ceasar": ["resolver"]
}
Admin
As an Admin, I can view every ticket.
Note that the filter returned is empty, which implies "show all". In UCAST this is represented with a {}
, and with SQL strings, this is ""
.
Request
- UCAST
- PostgreSQL
POST /v1/compile
Content-Type: application/json
Accept: application/vnd.styra.ucast.all+json
{
"query": "data.filters.include",
"input": {
"user": "alice",
"action": "list"
},
"unknowns": [
"input.tickets",
"input.users"
]
}
POST /v1/compile
Content-Type: application/json
Accept: application/vnd.styra.sql.postgresql+json
{
"query": "data.filters.include",
"input": {
"user": "alice",
"action": "list"
},
"unknowns": [
"input.tickets",
"input.users"
]
}
Response
- UCAST
- PostgreSQL
HTTP/1.1 200 OK
Content-Type: application/json
{
"result": {
"query": {}
}
}
HTTP/1.1 200 OK
Content-Type: application/json
{
"result": {
"query": ""
}
}
Reader
As a Reader, I can only see tickets that I am assigned to.
Request
- UCAST
- PostgreSQL
POST /v1/compile
Content-Type: application/json
Accept: application/vnd.styra.ucast.prisma+json
{
"query": "data.filters.include",
"input": {
"user": "bob",
"action": "list"
},
"unknowns": [
"input.tickets",
"input.users"
]
}
POST /v1/compile
Content-Type: application/json
Accept: application/vnd.styra.sql.postgresql+json
{
"query": "data.filters.include",
"input": {
"user": "bob",
"action": "list"
},
"unknowns": [
"input.tickets",
"input.users"
]
}
Response
- UCAST
- PostgreSQL
HTTP/1.1 200 OK
Content-Type: application/json
{
"result": {
"query": {
"type": "field",
"operator": "eq",
"field": "users.name",
"value": "bob"
}
}
}
HTTP/1.1 200 OK
Content-Type: application/json
{
"result": {
"query": "WHERE users.name = E'bob'"
}
}
Resolver (using Multitarget)
As a Resolver, I can see either tickets I'm assigned to, or tickets that are unassigned and unresolved
Request
- Multitarget: UCAST + PostgreSQL
POST /v1/compile
Content-Type: application/json
Accept: application/vnd.styra.multitarget+json
{
"query": "data.filters.include",
"input": {
"user": "ceasar",
"action": "list"
},
"unknowns": [
"input.tickets",
"input.users"
],
"options": {
"targetDialects": [
"ucast+prisma",
"sql+postgres"
]
}
}
Response
- Multitarget: UCAST + PostgreSQL
HTTP/1.1 200 OK
Content-Type: application/json
{
"result": {
"ucast": {
"query": {
"type": "compound",
"operator": "or",
"value": [
{
"type": "field",
"operator": "eq",
"field": "users.name",
"value": "ceasar"
},
{
"type": "compound",
"operator": "and",
"value": [
{
"type": "field",
"operator": "eq",
"field": "tickets.assignee",
"value": null
},
{
"type": "field",
"operator": "eq",
"field": "tickets.resolved",
"value": false
}
]
}
]
}
},
"postgres": {
"query": "WHERE (users.name = E'ceasar' OR (tickets.assignee IS NULL AND tickets.resolved = FALSE))"
}
}
}
Using Table and Column Remappings
In this example, we'll imagine the same scenario as above but the SQL schema is as follows:
The tickets table is now tbl_t
and the users table is now tbl_u
, and the assignee
joined field is now named assignedto
.
Request
- Multitarget: UCAST + PostgreSQL
POST /v1/compile
Content-Type: application/json
Accept: application/vnd.styra.multitarget+json
{
"query": "data.filters.include",
"input": {
"user": "ceasar",
"action": "list"
},
"unknowns": [
"input.tickets",
"input.users"
],
"options": {
"targetDialects": [
"ucast+prisma",
"sql+postgres"
],
"targetSQLTableMappings": {
"postgres": {
"users": {
"$self": "tbl_u"
},
"tickets": {
"$self": "tbl_t",
"assignee": "assignedto"
},
"ucast": {
"users": {
"$self": "tbl_u"
},
"tickets": {
"$self": "tbl_t",
"assignee": "assignedto"
}
}
}
}
}
Response
- Multitarget: UCAST + PostgreSQL
HTTP/1.1 200 OK
Content-Type: application/json
{
"result": {
"ucast": {
"query": {
"type": "compound",
"operator": "or",
"value": [
{
"type": "field",
"operator": "eq",
"field": "tbl_u.name",
"value": "ceasar"
},
{
"type": "compound",
"operator": "and",
"value": [
{
"type": "field",
"operator": "eq",
"field": "tbl_t.assignedto",
"value": null
},
{
"type": "field",
"operator": "eq",
"field": "tbl_t.resolved",
"value": false
}
]
}
]
}
},
"postgres": {
"query": "WHERE (tbl_u.name = E'ceasar' OR (tbl_t.assignedto IS NULL AND tbl_t.resolved = FALSE))"
}
}
}