PermDock
CLI

casl

Import CASL rules per role into a generated permissions and policy module - allows, inverted rules as denies, manage and all expanded, fields, and the Mongo-query condition subset as portable where - with every rule it cannot translate reported.

permdock casl import reads the rules a CASL ability holds for each role and writes one module with the permissions they name and a policy with one role per entry. It evaluates no code: the input is the JSON the rules already are.

permdock casl import --rules casl-rules.json --out src/policy.generated.ts
permdock casl import --rules casl-rules.json --out src/policy.generated.ts --json
FlagMeaning
--rules <file>JSON of { [role]: rules }: each role's ability.rules, or the arrays packRules writes
--out <file>The module to write: permissions, then policy with definePolicy and one role per input key

What it translates

CASLPermDock
{ action: 'read', subject: 'Post' }allow(permissions.post.read); the subject BlogPost becomes the resource blogPost
inverted: truedeny(…); a deny always wins, as cannot does
manage, allEvery action, and every subject, the rules name (create, read, update, delete when a subject names none)
fields: ['title']fields: ['title'] on the grant
conditions: { authorId: '${user.id}' }where: { authorId: principal.id }
$eq, $ne, $in, $nin, $lt, $lte, $gt, $gte, $exists, $and, $or, $noreq, ne, in, notIn, lt, lte, gt, gte, isNull, and, or, not

A create or list action is a collection action in the generated resource; every other action is an instance action. The generated policy maps { id, roles } as its subject; replace the subject mapper with the application's own.

What it reports

ReasonWhen
not-a-ruleAn entry is not a CASL raw rule
no-subjectA claim-only rule has no subject to grant on
not-an-identifierA subject or action cannot be a permission key
unsupported-conditionAn allow's condition uses $regex, $elemMatch, $all, $size, a nested field or a nested object; the allow is left out
deny-widenedThe same for an inverted rule: the deny is kept without its condition, so the import denies more, never less

--json prints { out, skipped: [{ role, rule, reason, detail }] }. Run testLegacyParity against the old ability before switching over.

Why

CASL keeps rules as data at run time (ability.rules), so an import can read them without running the application's code. Translating rules, not copying CASL's API, keeps PermDock's own model: references instead of strings, roles instead of per-call can definitions, and deny-overrides with three outcomes. A condition PermDock cannot express portably is reported instead of turned into a closure, because a closure would not reach a snapshot, RLS or an ORM filter. An inverted rule is widened rather than dropped because dropping a deny is the one translation error that grants access.

Last updated on

On this page