@warlock.js/access 5.27.0 → 5.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/llms-full.txt +25 -25
- package/llms.txt +5 -5
- package/package.json +7 -7
- package/skills/check-permissions/SKILL.md +4 -4
- package/skills/configure-access/SKILL.md +5 -5
- package/skills/define-policies/SKILL.md +3 -3
- package/skills/implement-resolver/SKILL.md +2 -2
- package/skills/manage-roles/SKILL.md +4 -4
- package/skills/overview/SKILL.md +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to `@warlock.js/access` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
|
|
6
6
|
|
|
7
|
+
## 5.29.0 - 2026-10-01
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Lockstep release; package APIs are unchanged. Skills refer to other topics by name instead of file paths, so cross-references keep working after `agent-kit` copies them into an app.
|
|
12
|
+
|
|
13
|
+
## 5.28.0 - 2026-10-01
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Lockstep release; package APIs are unchanged. `llms.txt` now lists every skill topic with its description (frontmatter in CRLF files was skipped).
|
|
18
|
+
|
|
7
19
|
## 5.27.0 - 2026-10-01
|
|
8
20
|
|
|
9
21
|
### Changed
|
package/llms-full.txt
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
name: check-permissions
|
|
11
|
-
description: 'Check permissions in `@warlock.js/access` — `can` / `cannot` / `canAll` / `canAny` (boolean), `authorize` / `authorizeAll` / `authorizeAny` (throw 403), and the `gate` / `gateAny` / `gateAll` route middleware. Class-level vs instance-level (pass a `resource` to run its policy). TRIGGER: `can(`, `authorize(`, `canAll`, `canAny`, `gate`, `gateAny`, `gateAll`, "check a permission", "protect route by permission", "403 forbidden", "any vs all permissions". Skip: ownership / tenant conditions —
|
|
11
|
+
description: 'Check permissions in `@warlock.js/access` — `can` / `cannot` / `canAll` / `canAny` (boolean), `authorize` / `authorizeAll` / `authorizeAny` (throw 403), and the `gate` / `gateAny` / `gateAll` route middleware. Class-level vs instance-level (pass a `resource` to run its policy). TRIGGER: `can(`, `authorize(`, `canAll`, `canAny`, `gate`, `gateAny`, `gateAll`, "check a permission", "protect route by permission", "403 forbidden", "any vs all permissions". Skip: ownership / tenant conditions — the `define-policies` topic; role checks — the `manage-roles` topic.'
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
# Check permissions
|
|
@@ -52,7 +52,7 @@ await authorizeAll(user, ["orders.update", "orders.viewCost"]);
|
|
|
52
52
|
|
|
53
53
|
## Instance-level — "this resource"
|
|
54
54
|
|
|
55
|
-
Pass a `resource` and the permission's policy runs on top of the grant (see
|
|
55
|
+
Pass a `resource` and the permission's policy runs on top of the grant (see the `define-policies` topic):
|
|
56
56
|
|
|
57
57
|
```ts
|
|
58
58
|
const order = await Order.find(orderId);
|
|
@@ -75,15 +75,15 @@ await authorize(user, "orders.update", { resource: order, tenant }); // grant AN
|
|
|
75
75
|
|
|
76
76
|
## See also
|
|
77
77
|
|
|
78
|
-
-
|
|
79
|
-
-
|
|
78
|
+
- The `define-policies` topic — the `{ resource }` conditions.
|
|
79
|
+
- The `protect-routes` topic of the `warlock-js-auth` skill — `authMiddleware`, which runs first.
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
## configure-access `@warlock.js/access/configure-access/SKILL.md`
|
|
83
83
|
|
|
84
84
|
---
|
|
85
85
|
name: configure-access
|
|
86
|
-
description: 'Configure `@warlock.js/access` — the REQUIRED `resolver` (the quickstart `DefaultAccessResolver` over a fixed code map vs the ejected, DB-backed `DatabaseAccessResolver`), `cache: { ttl }`, and the ambient tenant via the resolver''s optional `resolveTenant()`. TRIGGER: `AccessConfigurations`, `config.access`, `src/config/access.ts`, "set up permissions", "DefaultAccessResolver", "DatabaseAccessResolver", "access cache ttl", "access tenant". Skip: checking permissions —
|
|
86
|
+
description: 'Configure `@warlock.js/access` — the REQUIRED `resolver` (the quickstart `DefaultAccessResolver` over a fixed code map vs the ejected, DB-backed `DatabaseAccessResolver`), `cache: { ttl }`, and the ambient tenant via the resolver''s optional `resolveTenant()`. TRIGGER: `AccessConfigurations`, `config.access`, `src/config/access.ts`, "set up permissions", "DefaultAccessResolver", "DatabaseAccessResolver", "access cache ttl", "access tenant". Skip: checking permissions — the `check-permissions` topic; custom storage — the `implement-resolver` topic.'
|
|
87
87
|
---
|
|
88
88
|
|
|
89
89
|
# Configure access
|
|
@@ -127,7 +127,7 @@ const access: AccessConfigurations = {
|
|
|
127
127
|
export default access;
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
-
Assign roles via the ejected `UserRole.assign(user, "editor", tenant)` — it flushes that user's cached set for you (see
|
|
130
|
+
Assign roles via the ejected `UserRole.assign(user, "editor", tenant)` — it flushes that user's cached set for you (see the `manage-roles` topic).
|
|
131
131
|
|
|
132
132
|
## The full shape
|
|
133
133
|
|
|
@@ -145,7 +145,7 @@ type AccessConfigurations = {
|
|
|
145
145
|
|
|
146
146
|
An instance check (`{ resource }`) for a permission with no registered ABAC policy **denies by default** (`strictPolicies: true`) — a forgotten policy must never fail open. The miss is logged once per permission (`log.warn`) either way.
|
|
147
147
|
|
|
148
|
-
Set `strictPolicies: false` to explicitly restore the pre-5.13 behavior: fall back to the RBAC grant alone (ALLOWS — any holder of the permission can act on ANY resource, a plausible IDOR). See
|
|
148
|
+
Set `strictPolicies: false` to explicitly restore the pre-5.13 behavior: fall back to the RBAC grant alone (ALLOWS — any holder of the permission can act on ANY resource, a plausible IDOR). See the `define-policies` topic for the full decision table.
|
|
149
149
|
|
|
150
150
|
> **Breaking in 5.13:** `strictPolicies` now defaults to `true` and DENIES (it used to default to `false` and ALLOW). Set `strictPolicies: false` to keep the old fail-open fallback.
|
|
151
151
|
|
|
@@ -167,8 +167,8 @@ The engine caches each user's resolved set (`cache.ttl`, default `10m`). Roles c
|
|
|
167
167
|
|
|
168
168
|
## See also
|
|
169
169
|
|
|
170
|
-
-
|
|
171
|
-
-
|
|
170
|
+
- The `implement-resolver` topic — write a resolver for your own storage.
|
|
171
|
+
- The `manage-roles` topic — assign / revoke with the ejected table.
|
|
172
172
|
```
|
|
173
173
|
|
|
174
174
|
|
|
@@ -176,7 +176,7 @@ The engine caches each user's resolved set (`cache.ttl`, default `10m`). Roles c
|
|
|
176
176
|
|
|
177
177
|
---
|
|
178
178
|
name: define-policies
|
|
179
|
-
description: 'ABAC conditions in `@warlock.js/access` — `definePolicy(permission, (user, resource, ctx) => boolean)` adds an instance-level rule (ownership / tenant / state) on top of the RBAC grant, evaluated when an authorization check carries a `resource`. TRIGGER: `definePolicy`, "ownership check", "only their own", "can edit this specific record", "ABAC", "policy", "resource-level permission", `authorize(user, perm, { resource })`. Skip: plain grant checks —
|
|
179
|
+
description: 'ABAC conditions in `@warlock.js/access` — `definePolicy(permission, (user, resource, ctx) => boolean)` adds an instance-level rule (ownership / tenant / state) on top of the RBAC grant, evaluated when an authorization check carries a `resource`. TRIGGER: `definePolicy`, "ownership check", "only their own", "can edit this specific record", "ABAC", "policy", "resource-level permission", `authorize(user, perm, { resource })`. Skip: plain grant checks — the `check-permissions` topic.'
|
|
180
180
|
---
|
|
181
181
|
|
|
182
182
|
# Define policies (ABAC)
|
|
@@ -212,7 +212,7 @@ So `gate("orders.update")` (a route gate, no resource) checks the grant; the per
|
|
|
212
212
|
## The decision = grant AND policy
|
|
213
213
|
|
|
214
214
|
- No grant → denied (the policy never runs).
|
|
215
|
-
- Grant + no policy registered → **denied by default** (`strictPolicies: true`, the 5.13+ default) — a typo'd permission or a forgotten `import "./policies"` must never silently degrade an instance check to a class-level one. Logs a one-time `log.warn` naming the permission either way. Set `strictPolicies: false` in the access config (see
|
|
215
|
+
- Grant + no policy registered → **denied by default** (`strictPolicies: true`, the 5.13+ default) — a typo'd permission or a forgotten `import "./policies"` must never silently degrade an instance check to a class-level one. Logs a one-time `log.warn` naming the permission either way. Set `strictPolicies: false` in the access config (see the `configure-access` topic) to explicitly restore the pre-5.13 RBAC-only fallback (allowed).
|
|
216
216
|
- Grant + policy → the policy decides.
|
|
217
217
|
|
|
218
218
|
Policies **deny**, they don't grant — a policy can't let a user past a permission they don't hold.
|
|
@@ -241,14 +241,14 @@ definePolicy("orders.refund", (user, order, ctx) =>
|
|
|
241
241
|
|
|
242
242
|
## See also
|
|
243
243
|
|
|
244
|
-
-
|
|
244
|
+
- The `check-permissions` topic — `authorize(…, { resource })`.
|
|
245
245
|
|
|
246
246
|
|
|
247
247
|
## implement-resolver `@warlock.js/access/implement-resolver/SKILL.md`
|
|
248
248
|
|
|
249
249
|
---
|
|
250
250
|
name: implement-resolver
|
|
251
|
-
description: 'Connect `@warlock.js/access` to your role/permission storage by implementing the `AccessResolver` contract (`resolveRoles` / `resolvePermissions`, optional `resolveTenant`) — for a DB-backed catalog, a user column, a pivot table, a token claim, or an external directory. The engine owns matching / caching / policies; the resolver only fetches. TRIGGER: `AccessResolver`, `DatabaseAccessResolver`, `resolveTenant`, "custom resolver", "where do roles come from", "roles in a token claim", "permissions from an external API", "implement resolver". Skip: the quickstart `DefaultAccessResolver` —
|
|
251
|
+
description: 'Connect `@warlock.js/access` to your role/permission storage by implementing the `AccessResolver` contract (`resolveRoles` / `resolvePermissions`, optional `resolveTenant`) — for a DB-backed catalog, a user column, a pivot table, a token claim, or an external directory. The engine owns matching / caching / policies; the resolver only fetches. TRIGGER: `AccessResolver`, `DatabaseAccessResolver`, `resolveTenant`, "custom resolver", "where do roles come from", "roles in a token claim", "permissions from an external API", "implement resolver". Skip: the quickstart `DefaultAccessResolver` — the `configure-access` topic.'
|
|
252
252
|
---
|
|
253
253
|
|
|
254
254
|
# Implement a resolver
|
|
@@ -350,7 +350,7 @@ class DirectResolver implements AccessResolver {
|
|
|
350
350
|
|
|
351
351
|
## See also
|
|
352
352
|
|
|
353
|
-
-
|
|
353
|
+
- The `configure-access` topic — the quickstart `DefaultAccessResolver`.
|
|
354
354
|
```
|
|
355
355
|
|
|
356
356
|
|
|
@@ -358,7 +358,7 @@ class DirectResolver implements AccessResolver {
|
|
|
358
358
|
|
|
359
359
|
---
|
|
360
360
|
name: manage-roles
|
|
361
|
-
description: 'Assign and read roles in `@warlock.js/access` — the ejected `UserRole.assign` / `UserRole.revoke` (the `user_roles` table) followed by `access.flush`, plus `hasRole` / `hasAnyRole` / `hasAllRoles`. The role→permission catalog is the ejected `Role` table (dynamic). TRIGGER: `UserRole.assign`, `UserRole.revoke`, `access.flush`, `hasRole`, `hasAnyRole`, `hasAllRoles`, `Role` table, "give a user a role", "assign role", "check a user role", "roles per tenant". Skip: permission checks —
|
|
361
|
+
description: 'Assign and read roles in `@warlock.js/access` — the ejected `UserRole.assign` / `UserRole.revoke` (the `user_roles` table) followed by `access.flush`, plus `hasRole` / `hasAnyRole` / `hasAllRoles`. The role→permission catalog is the ejected `Role` table (dynamic). TRIGGER: `UserRole.assign`, `UserRole.revoke`, `access.flush`, `hasRole`, `hasAnyRole`, `hasAllRoles`, `Role` table, "give a user a role", "assign role", "check a user role", "roles per tenant". Skip: permission checks — the `check-permissions` topic; resolver choice — the `configure-access` topic.'
|
|
362
362
|
---
|
|
363
363
|
|
|
364
364
|
# Manage roles
|
|
@@ -420,12 +420,12 @@ The ejected `UserRole` is a thin cascade model — `{ user_id, user_type, role,
|
|
|
420
420
|
|
|
421
421
|
## Roles stored elsewhere?
|
|
422
422
|
|
|
423
|
-
If roles live on the user (a `roles` column) or in a token claim, you don't use `UserRole` — you manage assignment your way and a resolver reads them. See
|
|
423
|
+
If roles live on the user (a `roles` column) or in a token claim, you don't use `UserRole` — you manage assignment your way and a resolver reads them. See the `implement-resolver` topic.
|
|
424
424
|
|
|
425
425
|
## See also
|
|
426
426
|
|
|
427
|
-
-
|
|
428
|
-
-
|
|
427
|
+
- The `configure-access` topic — choosing the resolver.
|
|
428
|
+
- The `implement-resolver` topic — non-table role storage.
|
|
429
429
|
```
|
|
430
430
|
|
|
431
431
|
|
|
@@ -461,11 +461,11 @@ Skip if you only need "is the user an admin" — `authMiddleware("admin")` from
|
|
|
461
461
|
|
|
462
462
|
## Skills index
|
|
463
463
|
|
|
464
|
-
-
|
|
465
|
-
-
|
|
466
|
-
-
|
|
467
|
-
-
|
|
468
|
-
-
|
|
464
|
+
- `configure-access` — the required resolver (`DefaultAccessResolver` vs the ejected `DatabaseAccessResolver`), cache + tenant.
|
|
465
|
+
- `check-permissions` — `can` / `cannot` / `canAll` / `canAny` / `authorize*` + the `gate*` middleware.
|
|
466
|
+
- `define-policies` — `definePolicy` for ownership / tenant / state conditions (ABAC).
|
|
467
|
+
- `manage-roles` — assign / revoke via the ejected `UserRole` + `access.flush`, `hasRole` / `hasAnyRole` / `hasAllRoles`.
|
|
468
|
+
- `implement-resolver` — connect the engine to your storage (a DB catalog, a column, a pivot, a token claim).
|
|
469
469
|
|
|
470
470
|
## What it deliberately doesn't do
|
|
471
471
|
|
|
@@ -475,7 +475,7 @@ Skip if you only need "is the user an admin" — `authMiddleware("admin")` from
|
|
|
475
475
|
|
|
476
476
|
## See also
|
|
477
477
|
|
|
478
|
-
-
|
|
479
|
-
-
|
|
478
|
+
- The `overview` topic of the `warlock-js-auth` skill — authentication, the layer below.
|
|
479
|
+
- The `protect-routes` topic of the `warlock-js-auth` skill — `authMiddleware`; stack `gate` after it.
|
|
480
480
|
|
|
481
481
|
|
package/llms.txt
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
## Skills
|
|
8
8
|
|
|
9
|
-
- [check-permissions](@warlock.js/access/check-permissions/SKILL.md): (
|
|
10
|
-
- [configure-access](@warlock.js/access/configure-access/SKILL.md): (
|
|
11
|
-
- [define-policies](@warlock.js/access/define-policies/SKILL.md): (
|
|
12
|
-
- [implement-resolver](@warlock.js/access/implement-resolver/SKILL.md): (
|
|
13
|
-
- [manage-roles](@warlock.js/access/manage-roles/SKILL.md): (
|
|
9
|
+
- [check-permissions](@warlock.js/access/check-permissions/SKILL.md): Check permissions in `@warlock.js/access` — `can` / `cannot` / `canAll` / `canAny` (boolean), `authorize` / `authorizeAll` / `authorizeAny` (throw 403), and the `gate` / `gateAny` / `gateAll` route middleware. Class-level vs instance-level (pass a `resource` to run its policy). TRIGGER: `can(`, `authorize(`, `canAll`, `canAny`, `gate`, `gateAny`, `gateAll`, "check a permission", "protect route by permission", "403 forbidden", "any vs all permissions". Skip: ownership / tenant conditions — the `define-policies` topic; role checks — the `manage-roles` topic.
|
|
10
|
+
- [configure-access](@warlock.js/access/configure-access/SKILL.md): Configure `@warlock.js/access` — the REQUIRED `resolver` (the quickstart `DefaultAccessResolver` over a fixed code map vs the ejected, DB-backed `DatabaseAccessResolver`), `cache: { ttl }`, and the ambient tenant via the resolver's optional `resolveTenant()`. TRIGGER: `AccessConfigurations`, `config.access`, `src/config/access.ts`, "set up permissions", "DefaultAccessResolver", "DatabaseAccessResolver", "access cache ttl", "access tenant". Skip: checking permissions — the `check-permissions` topic; custom storage — the `implement-resolver` topic.
|
|
11
|
+
- [define-policies](@warlock.js/access/define-policies/SKILL.md): ABAC conditions in `@warlock.js/access` — `definePolicy(permission, (user, resource, ctx) => boolean)` adds an instance-level rule (ownership / tenant / state) on top of the RBAC grant, evaluated when an authorization check carries a `resource`. TRIGGER: `definePolicy`, "ownership check", "only their own", "can edit this specific record", "ABAC", "policy", "resource-level permission", `authorize(user, perm, { resource })`. Skip: plain grant checks — the `check-permissions` topic.
|
|
12
|
+
- [implement-resolver](@warlock.js/access/implement-resolver/SKILL.md): Connect `@warlock.js/access` to your role/permission storage by implementing the `AccessResolver` contract (`resolveRoles` / `resolvePermissions`, optional `resolveTenant`) — for a DB-backed catalog, a user column, a pivot table, a token claim, or an external directory. The engine owns matching / caching / policies; the resolver only fetches. TRIGGER: `AccessResolver`, `DatabaseAccessResolver`, `resolveTenant`, "custom resolver", "where do roles come from", "roles in a token claim", "permissions from an external API", "implement resolver". Skip: the quickstart `DefaultAccessResolver` — the `configure-access` topic.
|
|
13
|
+
- [manage-roles](@warlock.js/access/manage-roles/SKILL.md): Assign and read roles in `@warlock.js/access` — the ejected `UserRole.assign` / `UserRole.revoke` (the `user_roles` table) followed by `access.flush`, plus `hasRole` / `hasAnyRole` / `hasAllRoles`. The role→permission catalog is the ejected `Role` table (dynamic). TRIGGER: `UserRole.assign`, `UserRole.revoke`, `access.flush`, `hasRole`, `hasAnyRole`, `hasAllRoles`, `Role` table, "give a user a role", "assign role", "check a user role", "roles per tenant". Skip: permission checks — the `check-permissions` topic; resolver choice — the `configure-access` topic.
|
|
14
14
|
- [overview](@warlock.js/access/overview/SKILL.md): @warlock.js/access overview for orientation and choosing the focused skill for the task.
|
package/package.json
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
"environment": "server"
|
|
6
6
|
},
|
|
7
7
|
"peerDependencies": {
|
|
8
|
-
"@warlock.js/auth": "5.
|
|
9
|
-
"@warlock.js/cache": "5.
|
|
10
|
-
"@warlock.js/cascade": "5.
|
|
11
|
-
"@warlock.js/core": "5.
|
|
12
|
-
"@warlock.js/logger": "5.
|
|
13
|
-
"@warlock.js/seal": "5.
|
|
8
|
+
"@warlock.js/auth": "5.29.0",
|
|
9
|
+
"@warlock.js/cache": "5.29.0",
|
|
10
|
+
"@warlock.js/cascade": "5.29.0",
|
|
11
|
+
"@warlock.js/core": "5.29.0",
|
|
12
|
+
"@warlock.js/logger": "5.29.0",
|
|
13
|
+
"@warlock.js/seal": "5.29.0"
|
|
14
14
|
},
|
|
15
15
|
"repository": {
|
|
16
16
|
"type": "git",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
],
|
|
28
28
|
"author": "hassanzohdy",
|
|
29
29
|
"license": "MIT",
|
|
30
|
-
"version": "5.
|
|
30
|
+
"version": "5.29.0",
|
|
31
31
|
"main": "./cjs/index.cjs",
|
|
32
32
|
"module": "./esm/index.mjs",
|
|
33
33
|
"types": "./esm/index.d.mts",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: check-permissions
|
|
3
|
-
description: 'Check permissions in `@warlock.js/access` — `can` / `cannot` / `canAll` / `canAny` (boolean), `authorize` / `authorizeAll` / `authorizeAny` (throw 403), and the `gate` / `gateAny` / `gateAll` route middleware. Class-level vs instance-level (pass a `resource` to run its policy). TRIGGER: `can(`, `authorize(`, `canAll`, `canAny`, `gate`, `gateAny`, `gateAll`, "check a permission", "protect route by permission", "403 forbidden", "any vs all permissions". Skip: ownership / tenant conditions —
|
|
3
|
+
description: 'Check permissions in `@warlock.js/access` — `can` / `cannot` / `canAll` / `canAny` (boolean), `authorize` / `authorizeAll` / `authorizeAny` (throw 403), and the `gate` / `gateAny` / `gateAll` route middleware. Class-level vs instance-level (pass a `resource` to run its policy). TRIGGER: `can(`, `authorize(`, `canAll`, `canAny`, `gate`, `gateAny`, `gateAll`, "check a permission", "protect route by permission", "403 forbidden", "any vs all permissions". Skip: ownership / tenant conditions — the `define-policies` topic; role checks — the `manage-roles` topic.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Check permissions
|
|
@@ -44,7 +44,7 @@ await authorizeAll(user, ["orders.update", "orders.viewCost"]);
|
|
|
44
44
|
|
|
45
45
|
## Instance-level — "this resource"
|
|
46
46
|
|
|
47
|
-
Pass a `resource` and the permission's policy runs on top of the grant (see
|
|
47
|
+
Pass a `resource` and the permission's policy runs on top of the grant (see the `define-policies` topic):
|
|
48
48
|
|
|
49
49
|
```ts
|
|
50
50
|
const order = await Order.find(orderId);
|
|
@@ -67,5 +67,5 @@ await authorize(user, "orders.update", { resource: order, tenant }); // grant AN
|
|
|
67
67
|
|
|
68
68
|
## See also
|
|
69
69
|
|
|
70
|
-
-
|
|
71
|
-
-
|
|
70
|
+
- The `define-policies` topic — the `{ resource }` conditions.
|
|
71
|
+
- The `protect-routes` topic of the `warlock-js-auth` skill — `authMiddleware`, which runs first.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: configure-access
|
|
3
|
-
description: 'Configure `@warlock.js/access` — the REQUIRED `resolver` (the quickstart `DefaultAccessResolver` over a fixed code map vs the ejected, DB-backed `DatabaseAccessResolver`), `cache: { ttl }`, and the ambient tenant via the resolver''s optional `resolveTenant()`. TRIGGER: `AccessConfigurations`, `config.access`, `src/config/access.ts`, "set up permissions", "DefaultAccessResolver", "DatabaseAccessResolver", "access cache ttl", "access tenant". Skip: checking permissions —
|
|
3
|
+
description: 'Configure `@warlock.js/access` — the REQUIRED `resolver` (the quickstart `DefaultAccessResolver` over a fixed code map vs the ejected, DB-backed `DatabaseAccessResolver`), `cache: { ttl }`, and the ambient tenant via the resolver''s optional `resolveTenant()`. TRIGGER: `AccessConfigurations`, `config.access`, `src/config/access.ts`, "set up permissions", "DefaultAccessResolver", "DatabaseAccessResolver", "access cache ttl", "access tenant". Skip: checking permissions — the `check-permissions` topic; custom storage — the `implement-resolver` topic.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Configure access
|
|
@@ -44,7 +44,7 @@ const access: AccessConfigurations = {
|
|
|
44
44
|
export default access;
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
Assign roles via the ejected `UserRole.assign(user, "editor", tenant)` — it flushes that user's cached set for you (see
|
|
47
|
+
Assign roles via the ejected `UserRole.assign(user, "editor", tenant)` — it flushes that user's cached set for you (see the `manage-roles` topic).
|
|
48
48
|
|
|
49
49
|
## The full shape
|
|
50
50
|
|
|
@@ -62,7 +62,7 @@ type AccessConfigurations = {
|
|
|
62
62
|
|
|
63
63
|
An instance check (`{ resource }`) for a permission with no registered ABAC policy **denies by default** (`strictPolicies: true`) — a forgotten policy must never fail open. The miss is logged once per permission (`log.warn`) either way.
|
|
64
64
|
|
|
65
|
-
Set `strictPolicies: false` to explicitly restore the pre-5.13 behavior: fall back to the RBAC grant alone (ALLOWS — any holder of the permission can act on ANY resource, a plausible IDOR). See
|
|
65
|
+
Set `strictPolicies: false` to explicitly restore the pre-5.13 behavior: fall back to the RBAC grant alone (ALLOWS — any holder of the permission can act on ANY resource, a plausible IDOR). See the `define-policies` topic for the full decision table.
|
|
66
66
|
|
|
67
67
|
> **Breaking in 5.13:** `strictPolicies` now defaults to `true` and DENIES (it used to default to `false` and ALLOW). Set `strictPolicies: false` to keep the old fail-open fallback.
|
|
68
68
|
|
|
@@ -84,6 +84,6 @@ The engine caches each user's resolved set (`cache.ttl`, default `10m`). Roles c
|
|
|
84
84
|
|
|
85
85
|
## See also
|
|
86
86
|
|
|
87
|
-
-
|
|
88
|
-
-
|
|
87
|
+
- The `implement-resolver` topic — write a resolver for your own storage.
|
|
88
|
+
- The `manage-roles` topic — assign / revoke with the ejected table.
|
|
89
89
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: define-policies
|
|
3
|
-
description: 'ABAC conditions in `@warlock.js/access` — `definePolicy(permission, (user, resource, ctx) => boolean)` adds an instance-level rule (ownership / tenant / state) on top of the RBAC grant, evaluated when an authorization check carries a `resource`. TRIGGER: `definePolicy`, "ownership check", "only their own", "can edit this specific record", "ABAC", "policy", "resource-level permission", `authorize(user, perm, { resource })`. Skip: plain grant checks —
|
|
3
|
+
description: 'ABAC conditions in `@warlock.js/access` — `definePolicy(permission, (user, resource, ctx) => boolean)` adds an instance-level rule (ownership / tenant / state) on top of the RBAC grant, evaluated when an authorization check carries a `resource`. TRIGGER: `definePolicy`, "ownership check", "only their own", "can edit this specific record", "ABAC", "policy", "resource-level permission", `authorize(user, perm, { resource })`. Skip: plain grant checks — the `check-permissions` topic.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Define policies (ABAC)
|
|
@@ -36,7 +36,7 @@ So `gate("orders.update")` (a route gate, no resource) checks the grant; the per
|
|
|
36
36
|
## The decision = grant AND policy
|
|
37
37
|
|
|
38
38
|
- No grant → denied (the policy never runs).
|
|
39
|
-
- Grant + no policy registered → **denied by default** (`strictPolicies: true`, the 5.13+ default) — a typo'd permission or a forgotten `import "./policies"` must never silently degrade an instance check to a class-level one. Logs a one-time `log.warn` naming the permission either way. Set `strictPolicies: false` in the access config (see
|
|
39
|
+
- Grant + no policy registered → **denied by default** (`strictPolicies: true`, the 5.13+ default) — a typo'd permission or a forgotten `import "./policies"` must never silently degrade an instance check to a class-level one. Logs a one-time `log.warn` naming the permission either way. Set `strictPolicies: false` in the access config (see the `configure-access` topic) to explicitly restore the pre-5.13 RBAC-only fallback (allowed).
|
|
40
40
|
- Grant + policy → the policy decides.
|
|
41
41
|
|
|
42
42
|
Policies **deny**, they don't grant — a policy can't let a user past a permission they don't hold.
|
|
@@ -65,4 +65,4 @@ definePolicy("orders.refund", (user, order, ctx) =>
|
|
|
65
65
|
|
|
66
66
|
## See also
|
|
67
67
|
|
|
68
|
-
-
|
|
68
|
+
- The `check-permissions` topic — `authorize(…, { resource })`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: implement-resolver
|
|
3
|
-
description: 'Connect `@warlock.js/access` to your role/permission storage by implementing the `AccessResolver` contract (`resolveRoles` / `resolvePermissions`, optional `resolveTenant`) — for a DB-backed catalog, a user column, a pivot table, a token claim, or an external directory. The engine owns matching / caching / policies; the resolver only fetches. TRIGGER: `AccessResolver`, `DatabaseAccessResolver`, `resolveTenant`, "custom resolver", "where do roles come from", "roles in a token claim", "permissions from an external API", "implement resolver". Skip: the quickstart `DefaultAccessResolver` —
|
|
3
|
+
description: 'Connect `@warlock.js/access` to your role/permission storage by implementing the `AccessResolver` contract (`resolveRoles` / `resolvePermissions`, optional `resolveTenant`) — for a DB-backed catalog, a user column, a pivot table, a token claim, or an external directory. The engine owns matching / caching / policies; the resolver only fetches. TRIGGER: `AccessResolver`, `DatabaseAccessResolver`, `resolveTenant`, "custom resolver", "where do roles come from", "roles in a token claim", "permissions from an external API", "implement resolver". Skip: the quickstart `DefaultAccessResolver` — the `configure-access` topic.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Implement a resolver
|
|
@@ -102,5 +102,5 @@ class DirectResolver implements AccessResolver {
|
|
|
102
102
|
|
|
103
103
|
## See also
|
|
104
104
|
|
|
105
|
-
-
|
|
105
|
+
- The `configure-access` topic — the quickstart `DefaultAccessResolver`.
|
|
106
106
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: manage-roles
|
|
3
|
-
description: 'Assign and read roles in `@warlock.js/access` — the ejected `UserRole.assign` / `UserRole.revoke` (the `user_roles` table) followed by `access.flush`, plus `hasRole` / `hasAnyRole` / `hasAllRoles`. The role→permission catalog is the ejected `Role` table (dynamic). TRIGGER: `UserRole.assign`, `UserRole.revoke`, `access.flush`, `hasRole`, `hasAnyRole`, `hasAllRoles`, `Role` table, "give a user a role", "assign role", "check a user role", "roles per tenant". Skip: permission checks —
|
|
3
|
+
description: 'Assign and read roles in `@warlock.js/access` — the ejected `UserRole.assign` / `UserRole.revoke` (the `user_roles` table) followed by `access.flush`, plus `hasRole` / `hasAnyRole` / `hasAllRoles`. The role→permission catalog is the ejected `Role` table (dynamic). TRIGGER: `UserRole.assign`, `UserRole.revoke`, `access.flush`, `hasRole`, `hasAnyRole`, `hasAllRoles`, `Role` table, "give a user a role", "assign role", "check a user role", "roles per tenant". Skip: permission checks — the `check-permissions` topic; resolver choice — the `configure-access` topic.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Manage roles
|
|
@@ -62,10 +62,10 @@ The ejected `UserRole` is a thin cascade model — `{ user_id, user_type, role,
|
|
|
62
62
|
|
|
63
63
|
## Roles stored elsewhere?
|
|
64
64
|
|
|
65
|
-
If roles live on the user (a `roles` column) or in a token claim, you don't use `UserRole` — you manage assignment your way and a resolver reads them. See
|
|
65
|
+
If roles live on the user (a `roles` column) or in a token claim, you don't use `UserRole` — you manage assignment your way and a resolver reads them. See the `implement-resolver` topic.
|
|
66
66
|
|
|
67
67
|
## See also
|
|
68
68
|
|
|
69
|
-
-
|
|
70
|
-
-
|
|
69
|
+
- The `configure-access` topic — choosing the resolver.
|
|
70
|
+
- The `implement-resolver` topic — non-table role storage.
|
|
71
71
|
```
|
package/skills/overview/SKILL.md
CHANGED
|
@@ -28,11 +28,11 @@ Skip if you only need "is the user an admin" — `authMiddleware("admin")` from
|
|
|
28
28
|
|
|
29
29
|
## Skills index
|
|
30
30
|
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
31
|
+
- `configure-access` — the required resolver (`DefaultAccessResolver` vs the ejected `DatabaseAccessResolver`), cache + tenant.
|
|
32
|
+
- `check-permissions` — `can` / `cannot` / `canAll` / `canAny` / `authorize*` + the `gate*` middleware.
|
|
33
|
+
- `define-policies` — `definePolicy` for ownership / tenant / state conditions (ABAC).
|
|
34
|
+
- `manage-roles` — assign / revoke via the ejected `UserRole` + `access.flush`, `hasRole` / `hasAnyRole` / `hasAllRoles`.
|
|
35
|
+
- `implement-resolver` — connect the engine to your storage (a DB catalog, a column, a pivot, a token claim).
|
|
36
36
|
|
|
37
37
|
## What it deliberately doesn't do
|
|
38
38
|
|
|
@@ -42,5 +42,5 @@ Skip if you only need "is the user an admin" — `authMiddleware("admin")` from
|
|
|
42
42
|
|
|
43
43
|
## See also
|
|
44
44
|
|
|
45
|
-
-
|
|
46
|
-
-
|
|
45
|
+
- The `overview` topic of the `warlock-js-auth` skill — authentication, the layer below.
|
|
46
|
+
- The `protect-routes` topic of the `warlock-js-auth` skill — `authMiddleware`; stack `gate` after it.
|