Skip to content

Policy Types

Kerberos.js supports three policy types:

  • resourcePolicy: selected by resource.kind, resource.policyVersion, and resource.scope
  • principalPolicy: selected by principal.id, principal.policyVersion, and principal.scope
  • rolePolicy: selected by each principal.roles[], principal.policyVersion, and principal.scope

You can pass either type on its own or mix them in the same constructor call:

javascript
const kerberos = new Kerberos(
  [
    expenseResourcePolicy,
    sallyPrincipalPolicy,
    userRolePolicy,
  ],
  [commonRoles]
);

ResourcePolicy

ResourcePolicy is the workhorse policy type, selected by resource.kind. Rules are matched by action, then by roles or derivedRoles, and may also use conditions, variables, constants, outputs, versions, and scopes — see the Quick Start for a complete example.

PrincipalPolicy

PrincipalPolicy follows the Cerbos-style model for principal-specific overrides. It is bound to a single principal and targets resource + action directly instead of roles / derivedRoles.

javascript
const sallyPrincipalPolicy = {
  principalPolicy: {
    principal: 'sally',
    version: 'default',
    scope: 'acme.corp',
    constants: {
      restrictedVendor: 'Flux Water Gear',
    },
    variables: {
      isRestrictedVendor: ({ R, C }) => R.attr.vendor === C.restrictedVendor,
    },
    rules: [
      {
        resource: 'expense',
        actions: [
          {
            name: 'deny-restricted-vendor-view',
            action: 'view',
            effect: Effect.Deny,
            condition: {
              match: ({ V }) => V.isRestrictedVendor,
            },
          },
          {
            name: 'allow-delete-override',
            action: 'delete',
            effect: Effect.Allow,
          },
        ],
      },
    ],
  },
};

RolePolicy

RolePolicy follows the Cerbos-style role-centric model. It is bound to a single role, targets resource + allowActions, and behaves as an allowlist for matching resources. If a matching role policy exists for the current resource and the action is not listed in allowActions, Kerberos returns EFFECT_DENY for that role layer.

javascript
const userRolePolicy = {
  rolePolicy: {
    role: 'USER',
    version: 'default',
    scope: 'acme.corp',
    constants: {
      restrictedVendor: 'Flux Water Gear',
    },
    variables: {
      isRestrictedVendor: ({ R, C }) => R.attr.vendor === C.restrictedVendor,
    },
    rules: [
      {
        resource: 'expense',
        allowActions: ['create'],
      },
      {
        resource: 'expense',
        allowActions: ['view'],
        condition: {
          match: ({ V }) => V.isRestrictedVendor === false,
        },
      },
    ],
  },
};

RolePolicy also supports parentRoles. When present, the child role can only keep actions that are also allowed by each locally defined parent role policy. Missing parent role policies are treated as external IdP roles and do not impose extra constraints inside Kerberos.

Mixed Policy Evaluation

When mixed policy types are present, Kerberos resolves each action in this order:

  1. Find the matching PrincipalPolicy for the request principal.
  2. If it returns an explicit EFFECT_ALLOW or EFFECT_DENY, use that result.
  3. Otherwise, evaluate all matching RolePolicy entries for the principal roles.
  4. If multiple role policies apply to the same action, EFFECT_DENY wins over EFFECT_ALLOW.
  5. If the role layer is not applicable for that action, fall back to the matching ResourcePolicy. Before its rules are matched, the imported derived roles are resolved: condition-backed definitions evaluate synchronously, and relation-backed definitions (the relation: field) resolve through the configured relations resolver (ReBAC) — list-first with parallel check fallback, one shared memo per request. The resulting effectiveDerivedRoles then participate in rule matching alongside plain roles.
  6. If nothing matches, return EFFECT_DENY.

The decision is computed per action — different actions in the same request may be resolved by different policy layers. Each lookup (principal / role / resource) walks the scope search chain and policyVersion, and checks in-memory policies first, then the optional cache.

Within the role layer

every RolePolicy matching a principal.roles[] entry is evaluated; EFFECT_DENY wins over EFFECT_ALLOW. When a role declares parentRoles, the child keeps only the actions that are also allowed by each locally defined parent role policy (intersection).

This keeps Kerberos.js aligned with the Cerbos-style principal override model described in the Cerbos principal policies documentation while extending the runtime with role-centric policy evaluation similar to Cerbos role policies.

Released under the MIT License.