Scoped policies

Scoped Policies are optional and are only evaluated if a "scope" is passed in the request, and there are matching "scope" attributes defined in the policies.
Resource and principal policies can define "scopePermissions", which affects how rules are applied across scopes. See the scope permissions documentation for more details.

Scoped policies offer a way to model hierarchical relationships that regularly occur in many situations. Typically, the requirement is to have a base set of policies that can then be overridden for specific cases. For example, a multi-tenant SaaS system could have a standard set of access rules that can then be customised to suit the requirements of different tenants. Another example is a large organization that might want to have regional or departmental customisations to their global access rules.

hierarchy

Cerbos resource and principal policies have an optional scope field that can be used to indicate that they are part of a set of policies that must be evaluated together. Additionally, resource and principal policies within the same scope must use the same scopePermissions setting to define how rules interact across scope levels.

apiVersion: api.cerbos.dev/v1
resourcePolicy:
  version: "default"
  scope: "acme.corp" (1)
  scopePermissions: SCOPE_PERMISSIONS_OVERRIDE_PARENT (2)
  resource: "album:object"
  rules:
    - actions: ['*']
      effect: EFFECT_ALLOW
      roles: ["admin"]
yaml
1 Scope definition
2 Scope permissions setting

The value of scope is a dot-separated string where each dotted segment defines an ancestor. During policy evaluation, the Cerbos engine starts with the most specific scoped policy and moves up the hierarchy. NOTE: The value of the scopePermissions field affects the policy evaluation behaviour. See scope permissions for more information. For example, consider a policy with the scope a.b.c. The Cerbos engine could process up to four policies to arrive at the final decision:

  • scope a.b.c

  • scope a.b

  • scope a

  • scope `` (no scope)

To illustrate, consider the following Check request:

{
  "requestId":  "test01",
  "actions":  ["view", "comment"],
  "resource":  {
    "kind":  "album:object",
    "policyVersion": "default",
    "scope": "customer.abc", (1)
    "instances": {
      "XX125": {
        "attr":  {
          "owner":  "alicia",
          "public": false,
          "tags": ["x", "y"],
        }
      }
    }
  },
  "principal":  {
    "id":  "alicia",
    "policyVersion": "default",
    "scope": "customer", (2)
    "roles":  ["user"],
    "attr": {
      "geography": "GB"
    }
  }
}
json
1 Optional resource scope
2 Optional principal scope

When processing the above request, the decision flow chart for the Cerbos engine would look like the following:

decision flow

Working with scoped policies

  • The policy without any scope defined is always the base policy. It is used by default if a request does not specify any scope.

  • Scope permissions must be consistent within the same scope. If conflicting scopePermissions settings are detected in policies within a shared scope, a build-time error will occur.

  • Scope traversal behaviour depends on scopePermissions:

    • With SCOPE_PERMISSIONS_OVERRIDE_PARENT, the first policy to return a decision wins for each action.

    • With SCOPE_PERMISSIONS_REQUIRE_PARENTAL_CONSENT_FOR_ALLOWS, leaf nodes can only restrict access and must conform to parent permissions.

  • There must be no gaps in the policy chain. For example, if you define a policy with scope a.b.c, then policies with scopes a.b, a, and no-scope should also exist in the policy repository.

  • Schemas must be the same among all the policies in the chain. The schemas used to validate the request are taken from the base policy (policy without a scope). Schemas defined in other policies of the chain will be ignored.

  • First match wins (when using SCOPE_PERMISSIONS_OVERRIDE_PARENT): Scoped policies are evaluated from the most specific to the least specific. The first policy to produce a decision (ALLOW/DENY) for an action is the winner. The remaining policies cannot override the decision for that particular action.

  • Parent constraints apply (when using SCOPE_PERMISSIONS_REQUIRE_PARENTAL_CONSENT_FOR_ALLOWS): The most specific policies can only restrict permissions further, not grant new ones.

  • Explicit imports for derived roles and variables: Variables and derived roles imports are not inherited between policies. Explicitly import any derived roles and re-define any variables in each policy that requires them.

  • Unless lenient scope search is enabled, a policy file matching the exact scope requested in the API request must exist in the store.