Org rules
Narrow the product's policy for one client, without changing the product or adding code.
Sooner or later one client asks for a rule of its own: "only the executive sees executive salaries", "leave for the board is approved outside the system". Adding if orgId == "acme" to code, or a condition every client gets, are both wrong. An org rule is the right tool: a deny that applies to one org only, compiled into a Cerbos scoped policy.
org: org_acme
rules:
executive_salaries:
description: Salaries in the executive unit are visible only to the person themselves.
deny: [employee:read:salary]
when: R.attr.orgUnitId == "exec" && R.attr.ownerId != P.idList the files in zaccess.yaml (a glob may match nothing):
orgRules: [./access/orgs/*.yaml]What they can do
- Only deny. An org rule can't grant anything, whatever its condition says. The generated scoped policy only takes access away.
- Only product permissions.
access.*can't be narrowed, so administration works the same in every org. - Only required attributes in
when, with the same variables as action conditions. A missing optional value would make the condition fail, which would skip the deny.
Where they apply
Everywhere a decision is made: record checks, list filters (the rule's negation joins the WHERE clause), and Explain, which names the rule with its description. Nothing in the product's code changes: the SDKs send the org as the Cerbos scope for kinds that org has rules for.
How they're built
zaccess gen compiles each org and kind with rules into policies/orgs/<org>/<kind>.yaml, a resource policy with scope: <org> that requires the product policy's allow as well (SCOPE_PERMISSIONS_REQUIRE_PARENTAL_CONSENT_FOR_ALLOWS). If that file is missing from what Cerbos serves, every check of that kind in that org is denied; leave lenientScopeSearch off so it never falls back to the product policy.
Testing them
Policy tests set scope: <org> on the resources:
resources:
exec_salary:
id: e_1
kind: employee
scope: org_acme # the SDKs send this for orgs with rules
attr: { orgId: org_acme, ownerId: u_exec, ownerPath: /u_ceo/u_exec/, orgUnitId: exec }conformance/policy-tests/org_rules_test.yaml in zynactra/access is a complete suite.
Cover the denied case, the case the rule leaves alone (the person themselves), and the same record in another org, where the rule must not apply.
Org rules change by deploy, like every policy. Rules that clients edit themselves are not supported.