OPA / Rego¶
Sluice can decide with Rego modules. Open Policy Agent (OPA) runs inside Sluice: you need no OPA server.
Example¶
Goal: analysts read the shop catalog, but only the rows of their tenant.
policies:
# Data sources and subject bindings still come from this folder.
directory: "./policies.d"
engine: opa
opa:
moduleDir: "./rego"
query: "data.sluice.main"
With engine: opa, Sluice evaluates no YAML policy. To combine both, see Policy engines.
package sluice
import rego.v1
default main := {
"allow": false,
"deny_reason": {"message": "only analysts read the shop catalog"},
}
# Analysts read the shop catalog. Each table gets a filter on the tenant of the subject.
main := {
"allow": true,
"row_filters": [tenant_filter(t.key) | some t in input.tables],
} if {
"analysts" in input.subject.groups
every t in input.tables {
t.catalog == "shop"
}
}
tenant_filter(table) := {
"table": table,
"predicate": {
"column": "tenant_id",
"op": "Equals",
"value": input.subject.claims.tenantId,
},
}
An analyst of the tenant acme reads the orders.
curl -s http://localhost:8080/v1/query \
-H "X-Api-Key: sl_demo_acme.supersecret" \
-H "Content-Type: application/json" \
-d '{"sql": "SELECT id, tenant_id, amount FROM shop.main.orders ORDER BY id"}'
{
"query_id": "<query-id>",
"columns": ["id", "tenant_id", "amount"],
"rows": [
[1, "acme", 120],
[2, "acme", 80]
],
"row_count": 2,
"truncated": false
}
A guest sends the same query.
curl -s http://localhost:8080/v1/query \
-H "X-Api-Key: sl_demo_guest.supersecret" \
-H "Content-Type: application/json" \
-d '{"sql": "SELECT id, tenant_id, amount FROM shop.main.orders ORDER BY id"}'
{
"code": "ACL_DENIED",
"message": "only analysts read the shop catalog",
"query_id": "<query-id>"
}
What happened: Sluice evaluated data.sluice.main for each query. For the analyst, the module returned allow and a row filter. For the guest, the default applied.
Fields¶
input key | Content |
|---|---|
subject | id, issuer, email, groups, auth_method, claims |
action | Statement kind, for example SELECT |
tables[] | catalog, schema, table, and key as catalog.schema.table |
shape | has_select_star, is_aggregate, has_cte, has_union, has_limit, limit, has_where, joins |
request | remote_ip, user_agent, and headers with names in lower case |
| Result field | Type | Default | Meaning |
|---|---|---|---|
allow | boolean | false | true allows. false denies. |
abstain | boolean | false | true: the other composite members decide. |
deny_reason | object | message, code, policy for the error. | |
row_filters[] | list | table (a key from the input), predicate as in a row filter, combine. | |
column_masks[] | list | table, column, and mask as in a column mask. | |
rejections[] | list | rule, message, code. One entry rejects the query. |
An undefined result denies the query. The header X-Sluice-Applied-Policies and the audit record show the name opa.
Limits¶
policies.opa.moduleDiris required. Sluice loads the.regofiles at its top level and does not start without one.- A module that does not compile fails the reload with
ERR_POLICY_INVALID. The old modules and policies stay active. - An unknown output field, a bad predicate or mask, or a table outside the input gives 503
ERR_POLICY_UNAVAILABLE. - An unknown
codebecomesACL_DENIEDorACL_REJECTED.