Skip to content

Conditions (ABAC) ​

← Documentation home

RBAC answers: "Does this user have a role that permits this action?" ABAC (Attribute-Based Access Control) adds: "...and do the attributes of the subject, resource, and environment satisfy extra predicates?"

In Sentinel, ABAC is implemented as conditions — predicate functions on EvaluationContext. Attach them with .when() on a RuleBuilder.


Evaluation context ​

Conditions receive one argument:

typescript
interface EvaluationContext<S> {
  subject: Subject<S>;
  action: InferAction<S>;
  resource: InferResource<S>;
  resourceContext: ResourceContext;
  tenantId?: string;
  environment?: Record<string, unknown>;
}

Use resourceContext for per-resource facts (owner id, status, amount). Use subject.attributes for user facts loaded at session time. Use environment for request-scoped data you inject before evaluation (IP, time, feature flags).


Sync conditions ​

typescript
allow()
  .roles("member")
  .actions("invoice:update")
  .on("invoice")
  .when(ctx => ctx.subject.id === ctx.resourceContext.ownerId)
  .when(ctx => ctx.resourceContext.status !== "finalized")
  .build();

All .when() calls on a rule are ANDed. There is no built-in OR across conditions on one rule — use two rules or combine logic inside one function.


Async conditions ​

When a condition needs I/O:

typescript
.when(async (ctx) => {
  const quota = await db.getExportQuota(ctx.subject.id);
  return quota.remaining > 0;
})

You must use the async evaluation methods — not the sync ones:

Sync (throws if async condition)Async
evaluate()evaluateAsync()
explain()explainAsync()
permitted()permittedAsync()

Fail-closed semantics ​

If a condition throws, it is treated as false. Access is not granted.

Register onConditionError to log without changing outcome:

typescript
const engine = new AccessEngine<AppSchema>({
  schema: {} as AppSchema,
  onConditionError: ({ ruleId, conditionIndex, error }) => {
    logger.warn("Condition error", { ruleId, conditionIndex, error });
  },
});

Conditions and caching ​

Evaluations that run conditions are not cached. Only unconditional paths can hit the LRU cache.

→ Evaluation cache


JSON policies ​

Serialized rules reference conditions by name. Register implementations in a ConditionRegistry at import time — the registry is your security boundary; arbitrary code is never loaded from JSON.

typescript
const conditions = new ConditionRegistry<AppSchema>();
conditions.register("isOwner", (ctx) => ctx.subject.id === ctx.resourceContext.ownerId);

const rules = importRulesFromJson<AppSchema>(storedJson, conditions);
engine.addRules(...rules);

→ JSON policy serialization · Serialization reference


Released under the MIT License.