ReBAC (OpenFGA)¶
A RelationshipPolicy asks OpenFGA, a server that stores relations, if a subject has a relation to a table. Without the relation, Sluice denies the query. This is ReBAC: relationship-based access control.
Example¶
Goal: only a viewer of a table reads it.
The policy runs only as the rebac member of a composite engine. See Policy engines.
apiVersion: sluice.bino.bi/v1beta1
kind: RelationshipPolicy
metadata:
name: shop-table-viewers
spec:
match:
any:
- resources:
catalogs: ["shop"]
backend:
endpoint: "http://localhost:8091"
storeId: "demo-store"
checks:
- objectTemplate: "table:{{catalog}}.{{schema}}.{{table}}"
relation: viewer
The example uses a stub in place of OpenFGA. The stub knows one tuple. A real model needs the type table with the relation viewer.
Alice reads the table.
curl -s http://localhost:8080/v1/query \
-H "X-Api-Key: sl_demo_alice.supersecret" \
-H "Content-Type: application/json" \
-d '{"sql": "SELECT id, country, amount FROM shop.main.orders ORDER BY id"}'
{
"query_id": "<query-id>",
"columns": ["id", "country", "amount"],
"rows": [
[1, "de", 120],
[2, "fr", 80]
],
"row_count": 2,
"truncated": false
}
Bob sends the same query.
curl -s http://localhost:8080/v1/query \
-H "X-Api-Key: sl_demo_bob.supersecret" \
-H "Content-Type: application/json" \
-d '{"sql": "SELECT id, country, amount FROM shop.main.orders ORDER BY id"}'
{
"code": "ACL_DENIED",
"message": "relationship check failed on shop.main.orders",
"query_id": "<query-id>",
"policy": "shop-table-viewers"
}
For Bob, Sluice sent this body to POST <endpoint>/stores/<storeId>/check.
What happened: Sluice ran each check for each table that the policy matches. OpenFGA answered allowed: false for Bob. A passed check allows nothing: Alice also needs the SqlAccessPolicy of the yaml member.
Fields¶
| Field | Type | Default | Meaning |
|---|---|---|---|
backend.endpoint | string | required | URL of the OpenFGA server. |
backend.storeId | string | required | Store id. |
backend.authorizationModelId | string | Model id for the check. | |
backend.tokenRef | string | Secret, sent as a Bearer token. | |
backend.timeout | duration | 3s | Timeout of one check. |
checks[].objectTemplate | string | required | The object. |
checks[].relation | string | required | The relation. |
checks[].subjectTemplate | string | user:{{subject.id}} | The user. |
Templates know {{catalog}}, {{schema}}, {{table}}, {{subject.id}} and {{subject.email}}. exclude takes single tables out of the check.
Sluice caches each answer for policies.rebac.cacheTtl (default 10s). A reload clears the cache.
Limits¶
- A check decides on a whole table. It does not filter rows.
- If OpenFGA fails, the query gets 503
ERR_POLICY_UNAVAILABLE. If it does not answer withinbackend.timeout, the query gets 504ERR_TIMEOUT. - Write placeholders without spaces. Sluice sends
{{ table }}and unknown placeholders as plain text. - With
enforcementMode: AuditorDryRun, a failed check does not deny. The policy is listed undershadow, withwould: denyfor a failed check. - The selector cannot use
resources.tags.