@dudousxd/nestjs-authz-codegen 0.1.0 → 0.3.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 +77 -0
- package/README.md +30 -11
- package/dist/index.d.ts +8 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +131 -55
- package/dist/index.js.map +1 -1
- package/package.json +23 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,82 @@
|
|
|
1
1
|
# @dudousxd/nestjs-authz-codegen
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#67](https://github.com/DavideCarvalho/nestjs-authz/pull/67) [`32e967f`](https://github.com/DavideCarvalho/nestjs-authz/commit/32e967ffbcd68a97e8effc77af9c16bbbafdc918) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Abilities of a policy without a `@PolicyFor(Resource)` link are now emitted model-less instead of
|
|
8
|
+
under a resource guessed from the class name (`InvoicePolicy` → `Invoice`). The `Gate` (and the
|
|
9
|
+
`can()` endpoint) resolves a string ability on a resource only through that link, so the guessed
|
|
10
|
+
keys could never resolve. Add `@PolicyFor(Invoice)` to keep the resource key.
|
|
11
|
+
|
|
12
|
+
- [#67](https://github.com/DavideCarvalho/nestjs-authz/pull/67) [`32e967f`](https://github.com/DavideCarvalho/nestjs-authz/commit/32e967ffbcd68a97e8effc77af9c16bbbafdc918) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Type client abilities from the official `@Can(Policy, 'ability', byId(Resource))`,
|
|
13
|
+
`@Permission` / `PermissionPolicy` permission names, `GatePolicy` abilities and
|
|
14
|
+
`@Policy()` / `@PolicyFor()` classes (skipping `before` and `scope`). The 0.8 string
|
|
15
|
+
`@Can('update', Post)` and `@Policy(Post)` are no longer read; a policy's resource comes from
|
|
16
|
+
`@PolicyFor(Resource)` (else its name). Peers on `@dudousxd/nestjs-authz` `>=0.9.0 <0.10.0`.
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- [#67](https://github.com/DavideCarvalho/nestjs-authz/pull/67) [`32e967f`](https://github.com/DavideCarvalho/nestjs-authz/commit/32e967ffbcd68a97e8effc77af9c16bbbafdc918) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Fix packages that could be published without files their `exports` point to. `tsc` builds no longer use a `.tsbuildinfo` that can outlive `dist/` and skip emitting files. A new `check:exports` step (also `prepack`) fails the release if any `exports`/`main`/`types` path is missing.
|
|
21
|
+
|
|
22
|
+
## 0.2.0
|
|
23
|
+
|
|
24
|
+
### Minor Changes
|
|
25
|
+
|
|
26
|
+
- [#9](https://github.com/DavideCarvalho/nestjs-authz/pull/9) [`07d01de`](https://github.com/DavideCarvalho/nestjs-authz/commit/07d01de286e7dfcae5fbeb10b7e8d48533214087) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Ecosystem improvements across the authz packages.
|
|
27
|
+
|
|
28
|
+
### Permissions
|
|
29
|
+
|
|
30
|
+
- **Wildcard / hierarchical permissions.** Permission strings now support wildcard
|
|
31
|
+
and hierarchical matching (e.g. `posts:*` or `posts:read:*`), so a single granted
|
|
32
|
+
permission can authorize a family of related actions instead of enumerating each one.
|
|
33
|
+
|
|
34
|
+
### Authorization decisions
|
|
35
|
+
|
|
36
|
+
- **Deny reason / message surfaced on `ForbiddenException`.** When a check fails, the
|
|
37
|
+
reason for the denial is propagated onto the thrown `ForbiddenException`, making it
|
|
38
|
+
possible to return a meaningful message to the caller and to debug authorization
|
|
39
|
+
failures.
|
|
40
|
+
- **Gate `after` hook.** The Gate now exposes an `after` hook that runs once a decision
|
|
41
|
+
has been made, enabling cross-cutting concerns such as auditing, logging, and metrics.
|
|
42
|
+
|
|
43
|
+
### Batch authorization
|
|
44
|
+
|
|
45
|
+
- **`allowsMany`** for evaluating multiple permission checks in a single call.
|
|
46
|
+
- **Batch `/authz/can` endpoint** so clients can resolve many checks in one round trip.
|
|
47
|
+
- **`createCanBatch`** client helper that batches `can` calls transparently.
|
|
48
|
+
|
|
49
|
+
### Performance
|
|
50
|
+
|
|
51
|
+
- **Request-scoped permission cache.** Permissions resolved during a request are cached
|
|
52
|
+
for the lifetime of that request, avoiding repeated lookups on the hot path.
|
|
53
|
+
|
|
54
|
+
### RBAC adapters (TypeORM)
|
|
55
|
+
|
|
56
|
+
- **Direct user permissions** granted to a user independent of their roles.
|
|
57
|
+
- **Tenant-scoped roles** so the same role can be assigned per tenant in multi-tenant
|
|
58
|
+
deployments.
|
|
59
|
+
|
|
60
|
+
### Query scoping / policy filter (ABAC)
|
|
61
|
+
|
|
62
|
+
- **ORM-neutral constraint AST.** Policies can produce a portable constraint
|
|
63
|
+
representation describing which rows a subject may access.
|
|
64
|
+
- **Per-ORM application.** The constraint AST is translated and applied for
|
|
65
|
+
**TypeORM**, **MikroORM**, and **Prisma**, giving ABAC-style query scoping that
|
|
66
|
+
filters data at the database layer regardless of the ORM in use.
|
|
67
|
+
|
|
68
|
+
### Testing
|
|
69
|
+
|
|
70
|
+
- **New `@dudousxd/nestjs-authz-testing` package** with fakes and helpers for testing
|
|
71
|
+
authorization in consumer applications.
|
|
72
|
+
- **Postgres / MySQL testcontainers + contract tests** so the ORM stores are verified
|
|
73
|
+
against real database engines, and a shared contract suite keeps the adapters
|
|
74
|
+
behaviorally consistent.
|
|
75
|
+
|
|
76
|
+
### Housekeeping
|
|
77
|
+
|
|
78
|
+
- **Packaging hygiene** across the published packages.
|
|
79
|
+
|
|
3
80
|
## 0.1.0
|
|
4
81
|
|
|
5
82
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
A [`@dudousxd/nestjs-codegen`](https://www.npmjs.com/package/@dudousxd/nestjs-codegen) **extension**
|
|
4
4
|
that discovers the authorization abilities declared with
|
|
5
5
|
[`@dudousxd/nestjs-authz`](https://github.com/DavideCarvalho/nestjs-authz)'s
|
|
6
|
-
`@Can(ability,
|
|
6
|
+
official `@Can(Policy, 'ability', ...)` decorators (from `@nestjs/authorization`), the
|
|
7
|
+
`@Permission(...)` sugar and, optionally, the abilities of `@Policy()` / `@PolicyFor(Resource)`
|
|
8
|
+
classes, and emits a typed `can()`
|
|
7
9
|
helper into your generated `api.ts` — so checking a permission on the frontend is a compile-time
|
|
8
10
|
type, not a magic string.
|
|
9
11
|
|
|
@@ -30,21 +32,30 @@ export default defineConfig({
|
|
|
30
32
|
|
|
31
33
|
## What it emits
|
|
32
34
|
|
|
33
|
-
Given controllers decorated with `@Can(...)`:
|
|
35
|
+
Given controllers decorated with the official `@Can(...)`:
|
|
34
36
|
|
|
35
37
|
```ts
|
|
36
38
|
@Controller('posts')
|
|
37
39
|
export class PostsController {
|
|
38
40
|
@Patch(':id')
|
|
39
|
-
@Can('update', Post)
|
|
41
|
+
@Can(PostPolicy, 'update', byId(Post)) // → 'update' on Post
|
|
40
42
|
update() {}
|
|
41
43
|
|
|
42
44
|
@Post(':id/publish')
|
|
43
|
-
@Can('access-admin')
|
|
45
|
+
@Can(GatePolicy, 'allows', ability('access-admin')) // → 'access-admin'
|
|
44
46
|
publish() {}
|
|
45
47
|
}
|
|
46
48
|
```
|
|
47
49
|
|
|
50
|
+
How each form maps to a client ability:
|
|
51
|
+
|
|
52
|
+
| Decorator | Client ability |
|
|
53
|
+
| --- | --- |
|
|
54
|
+
| `@Can(PostPolicy, 'update', byId(Post))` | `update` on `Post` (from `byId`, else the policy's `@PolicyFor` link, else `PostPolicy` → `Post`) |
|
|
55
|
+
| `@Can(PermissionPolicy, 'has', permission('posts.publish'))`, `@Permission('posts.publish')` | `posts.publish` |
|
|
56
|
+
| `@Can(GatePolicy, 'allows', ability('access-admin'))` | `access-admin` |
|
|
57
|
+
| `@Roles(...)`, `@Can(RolePolicy, ...)` | none (roles are not client abilities) |
|
|
58
|
+
|
|
48
59
|
the generated `api.ts` gains:
|
|
49
60
|
|
|
50
61
|
```ts
|
|
@@ -74,12 +85,20 @@ Routes carrying a `@Can` also expose a route-pinned `can()` handle member when a
|
|
|
74
85
|
|
|
75
86
|
## Server side
|
|
76
87
|
|
|
77
|
-
> **Per-instance decisions
|
|
78
|
-
>
|
|
79
|
-
>
|
|
80
|
-
> (`can('update', { type: 'Post', id })`) that misses the hydrated cache and
|
|
81
|
-
>
|
|
82
|
-
>
|
|
88
|
+
> **Per-instance decisions: hydrate, or register a resource loader.** The fetch fallback
|
|
89
|
+
> (`POST /authz/can`) receives a `{ type, id }` shim whose constructor is `Object`, so by default
|
|
90
|
+
> it only resolves **class-level abilities and ad-hoc gates** — a **resource-bound** ability
|
|
91
|
+
> (`can('update', { type: 'Post', id })`) that misses the hydrated cache and reaches the endpoint
|
|
92
|
+
> will **deny**. Two ways to make per-instance decisions resolve there:
|
|
93
|
+
>
|
|
94
|
+
> 1. **Hydrate (preferred, no request):** shared props / `authorizeResource` (tiers 1-2 of
|
|
95
|
+
> `@dudousxd/nestjs-authz-inertia`).
|
|
96
|
+
> 2. **Register `resourceLoaders` in core** so the endpoint rehydrates `{ type, id }` into the real
|
|
97
|
+
> entity before authorizing:
|
|
98
|
+
> `AuthzModule.forRoot({ canEndpoint: true, resourceLoaders: { Post: (id) => postRepo.findOneBy({ id: Number(id) }) } })`.
|
|
99
|
+
> With a loader registered the instance `@Policy` matches by constructor and decides correctly
|
|
100
|
+
> (a loader returning nullish → deny / not found). Without a loader for the `type`, the prior
|
|
101
|
+
> class-level-only behavior stands.
|
|
83
102
|
|
|
84
103
|
The runtime `can()` POSTs `{ ability, resource? }` to the configured `endpoint` and expects
|
|
85
104
|
`{ allowed: boolean }` back. Expose a tiny controller in your NestJS app that delegates to the
|
|
@@ -97,7 +116,7 @@ async can(@Body() body: { ability: string; resource?: { type: string; id?: strin
|
|
|
97
116
|
| Option | Default | Description |
|
|
98
117
|
| --- | --- | --- |
|
|
99
118
|
| `endpoint` | `'/authz/can'` | Server endpoint the generated `can()` POSTs to (expects `{ allowed }`). |
|
|
100
|
-
| `discoverPolicies` | `true` | Also
|
|
119
|
+
| `discoverPolicies` | `true` | Also list the methods of `@Policy()` / `@PolicyFor(Resource)` classes (but `before` and `scope`) as abilities. |
|
|
101
120
|
|
|
102
121
|
## Caveats
|
|
103
122
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import type { CodegenExtension } from '@dudousxd/nestjs-codegen/extension';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* `@
|
|
6
|
-
*
|
|
3
|
+
* A client ability discovered on a controller route (the official `@Can(Policy, 'ability', ...)`,
|
|
4
|
+
* or `@Permission`), or on a policy class (`@Policy()`,
|
|
5
|
+
* `@PolicyFor(Resource)`). Read statically from the AST: reflect-metadata is not available at
|
|
6
|
+
* codegen time.
|
|
7
7
|
*/
|
|
8
8
|
export interface AuthzAbilityInfo {
|
|
9
9
|
/** The ability string, e.g. `'update'` or `'access-admin'`. */
|
|
10
10
|
ability: string;
|
|
11
|
-
/** The resource class name the ability targets
|
|
11
|
+
/** The resource class name the ability targets (from `byId(Resource)` or the policy's link). */
|
|
12
12
|
resource?: string;
|
|
13
13
|
}
|
|
14
14
|
/** Options for {@link nestjsAuthzCodegen}. */
|
|
@@ -21,15 +21,15 @@ export interface AuthzCodegenOptions {
|
|
|
21
21
|
*/
|
|
22
22
|
endpoint?: string;
|
|
23
23
|
/**
|
|
24
|
-
* Also discover `@Policy(Resource)` classes and treat their public method names as class-level
|
|
24
|
+
* Also discover `@Policy()` / `@PolicyFor(Resource)` classes and treat their public method names as class-level
|
|
25
25
|
* abilities (so e.g. `PostPolicy.publish` contributes `'publish'` to the union even when no
|
|
26
26
|
* route is decorated with it yet). Default `true`.
|
|
27
27
|
*/
|
|
28
28
|
discoverPolicies?: boolean;
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
|
-
* `@dudousxd/nestjs-authz` codegen extension. Discovers every `@Can(ability,
|
|
32
|
-
* on your controllers (and, optionally, `@Policy`
|
|
31
|
+
* `@dudousxd/nestjs-authz` codegen extension. Discovers every official `@Can(Policy, 'ability', ...)`
|
|
32
|
+
* (and `@Permission`) on your controllers (and, optionally, the methods of `@Policy()` classes) and emits a typed `can()` helper
|
|
33
33
|
* into your generated `api.ts`: `ability` is a string-literal union of the discovered abilities, so a
|
|
34
34
|
* wrong ability is a compile error. The runtime `can()` delegates to `@dudousxd/nestjs-authz-client`
|
|
35
35
|
* (a shared `AbilityStore` + `createCan`) — so a hydrated decision answers with no request, and a
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAGjB,MAAM,oCAAoC,CAAC;AAG5C;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAGjB,MAAM,oCAAoC,CAAC;AAG5C;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,gGAAgG;IAChG,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAQD,8CAA8C;AAC9C,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAqPD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,mBAAwB,GAAG,gBAAgB,CAmEtF;AAED,eAAe,kBAAkB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,27 +1,87 @@
|
|
|
1
|
-
import { Node } from 'ts-morph';
|
|
1
|
+
import { Node, SyntaxKind } from 'ts-morph';
|
|
2
2
|
const DEFAULT_ENDPOINT = '/authz/can';
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
/** Built-in policies whose abilities are not client abilities themselves. */
|
|
4
|
+
const ROLE_POLICY = 'RolePolicy';
|
|
5
|
+
const PERMISSION_POLICY = 'PermissionPolicy';
|
|
6
|
+
const GATE_POLICY = 'GatePolicy';
|
|
7
|
+
/** The string literals among `args`, e.g. `permission('a', 'b')` → `['a', 'b']`. */
|
|
8
|
+
function stringArgs(args) {
|
|
9
|
+
return args.flatMap((a) => (Node.isStringLiteral(a) ? [a.getLiteralValue()] : []));
|
|
10
|
+
}
|
|
11
|
+
/** `byId(Post)` / `staticCanArgs('x')` → its callee name and arguments. */
|
|
12
|
+
function callOf(node) {
|
|
13
|
+
if (!node || !Node.isCallExpression(node))
|
|
14
|
+
return undefined;
|
|
15
|
+
return { name: node.getExpression().getText(), args: node.getArguments() };
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The resource a policy class is linked to with `@PolicyFor(Post)`, if any. No guess from the
|
|
19
|
+
* class name: the runtime `Gate` (and so the `can()` endpoint) resolves a string ability on a
|
|
20
|
+
* resource only through that link, so an unlinked policy's abilities are model-less.
|
|
21
|
+
*/
|
|
22
|
+
function resourceOfPolicy(policyName, project) {
|
|
23
|
+
for (const sourceFile of project.getSourceFiles()) {
|
|
24
|
+
const classDecl = sourceFile.getClass(policyName);
|
|
25
|
+
if (!classDecl)
|
|
26
|
+
continue;
|
|
27
|
+
const linked = resourceNameFromPolicy(classDecl);
|
|
28
|
+
if (linked)
|
|
29
|
+
return linked;
|
|
13
30
|
}
|
|
14
|
-
return
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Read the client abilities one `@Can(...)` (or sugar) decorator declares:
|
|
35
|
+
*
|
|
36
|
+
* - official `@Can(PostPolicy, 'update', byId(Post))` → `update` on `Post` (from `byId`, else
|
|
37
|
+
* the policy's `@PolicyFor` link, else model-less)
|
|
38
|
+
* - `@Can(PermissionPolicy, 'has', permission('posts.publish'))` / `@Permission('posts.publish')`
|
|
39
|
+
* → the named permissions, model-less
|
|
40
|
+
* - `@Can(GatePolicy, 'allows', ability('access-admin'))` → `access-admin`
|
|
41
|
+
*
|
|
42
|
+
* Roles (`@Roles`, `RolePolicy`) are not client abilities.
|
|
43
|
+
*/
|
|
44
|
+
function readCanDecorator(name, args, project) {
|
|
45
|
+
if (name === 'Permission')
|
|
46
|
+
return stringArgs(args).map((ability) => ({ ability }));
|
|
47
|
+
if (name !== 'Can')
|
|
48
|
+
return [];
|
|
49
|
+
const [first, second, third] = args;
|
|
50
|
+
if (!first)
|
|
51
|
+
return [];
|
|
52
|
+
if (!Node.isIdentifier(first) || !second || !Node.isStringLiteral(second))
|
|
53
|
+
return [];
|
|
54
|
+
const policy = first.getText();
|
|
55
|
+
const ability = second.getLiteralValue();
|
|
56
|
+
const resolver = callOf(third);
|
|
57
|
+
if (policy === ROLE_POLICY)
|
|
58
|
+
return [];
|
|
59
|
+
if (policy === PERMISSION_POLICY || policy === GATE_POLICY) {
|
|
60
|
+
return resolver ? stringArgs(resolver.args).map((a) => ({ ability: a })) : [];
|
|
61
|
+
}
|
|
62
|
+
const info = { ability };
|
|
63
|
+
const fromById = resolver?.name === 'byId' ? resolver.args[0] : undefined;
|
|
64
|
+
const resource = fromById && Node.isIdentifier(fromById)
|
|
65
|
+
? fromById.getText()
|
|
66
|
+
: resourceOfPolicy(policy, project);
|
|
67
|
+
if (resource)
|
|
68
|
+
info.resource = resource;
|
|
69
|
+
return [info];
|
|
70
|
+
}
|
|
71
|
+
/** Every client ability declared on a method or class, top-down. */
|
|
72
|
+
function abilitiesOn(decorated, project) {
|
|
73
|
+
return decorated
|
|
74
|
+
.getDecorators()
|
|
75
|
+
.flatMap((d) => readCanDecorator(d.getName(), d.getArguments(), project));
|
|
15
76
|
}
|
|
16
77
|
/**
|
|
17
|
-
* Discover the
|
|
18
|
-
*
|
|
19
|
-
* class's) `@Can` decorator. Class-level `@Can` applies when the method has none.
|
|
78
|
+
* Discover the abilities on the controller method backing a route, then on its class
|
|
79
|
+
* (the official guard applies both). The first one is the route's own ability.
|
|
20
80
|
*/
|
|
21
|
-
function
|
|
81
|
+
function discoverRouteAbilities(route, project) {
|
|
22
82
|
const ref = route.controllerRef;
|
|
23
83
|
if (!ref)
|
|
24
|
-
return
|
|
84
|
+
return [];
|
|
25
85
|
let sourceFile;
|
|
26
86
|
try {
|
|
27
87
|
sourceFile = project.addSourceFileAtPathIfExists(ref.filePath) ?? undefined;
|
|
@@ -32,44 +92,34 @@ function discoverRouteAbility(route, project) {
|
|
|
32
92
|
if (!sourceFile)
|
|
33
93
|
sourceFile = project.getSourceFile(ref.filePath);
|
|
34
94
|
if (!sourceFile)
|
|
35
|
-
return
|
|
95
|
+
return [];
|
|
36
96
|
const classDecl = sourceFile.getClass(ref.className);
|
|
37
97
|
if (!classDecl)
|
|
38
|
-
return
|
|
98
|
+
return [];
|
|
39
99
|
const method = classDecl.getMethod(ref.methodName);
|
|
40
|
-
|
|
41
|
-
const dec = method.getDecorators().find((d) => d.getName() === 'Can');
|
|
42
|
-
if (dec) {
|
|
43
|
-
const info = readCanDecorator(dec.getArguments());
|
|
44
|
-
if (info)
|
|
45
|
-
return info;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
// Fall back to a class-level `@Can(...)` guarding the whole controller.
|
|
49
|
-
const classDec = classDecl.getDecorators().find((d) => d.getName() === 'Can');
|
|
50
|
-
if (classDec)
|
|
51
|
-
return readCanDecorator(classDec.getArguments());
|
|
52
|
-
return null;
|
|
100
|
+
return [...(method ? abilitiesOn(method, project) : []), ...abilitiesOn(classDecl, project)];
|
|
53
101
|
}
|
|
102
|
+
/** Names that make a class a policy: the official `@Policy()` and `@PolicyFor(X)`. */
|
|
103
|
+
const POLICY_DECORATORS = new Set(['Policy', 'PolicyFor']);
|
|
104
|
+
/** Members of a policy that are not abilities. */
|
|
105
|
+
const NOT_ABILITIES = new Set(['before', 'scope', 'constructor']);
|
|
54
106
|
/**
|
|
55
|
-
* Discover
|
|
56
|
-
* method
|
|
57
|
-
*
|
|
107
|
+
* Discover the abilities of every policy class (`@Policy()`, `@PolicyFor(X)`): each public
|
|
108
|
+
* instance method but `before` and `scope`, keyed to the policy's `@PolicyFor` resource
|
|
109
|
+
* (model-less without one).
|
|
58
110
|
*/
|
|
59
111
|
function discoverPolicyAbilities(project) {
|
|
60
112
|
const found = [];
|
|
61
113
|
for (const sourceFile of project.getSourceFiles()) {
|
|
62
114
|
for (const classDecl of sourceFile.getClasses()) {
|
|
63
|
-
|
|
64
|
-
if (!policyDec)
|
|
115
|
+
if (!classDecl.getDecorators().some((d) => POLICY_DECORATORS.has(d.getName())))
|
|
65
116
|
continue;
|
|
66
|
-
const resource = resourceNameFromPolicy(
|
|
117
|
+
const resource = resourceNameFromPolicy(classDecl);
|
|
67
118
|
for (const method of classDecl.getMethods()) {
|
|
68
|
-
if (method.isStatic())
|
|
119
|
+
if (method.isStatic() || method.hasModifier(SyntaxKind.PrivateKeyword))
|
|
69
120
|
continue;
|
|
70
121
|
const name = method.getName();
|
|
71
|
-
|
|
72
|
-
if (name === 'before')
|
|
122
|
+
if (NOT_ABILITIES.has(name))
|
|
73
123
|
continue;
|
|
74
124
|
const ability = { ability: name };
|
|
75
125
|
if (resource)
|
|
@@ -80,14 +130,15 @@ function discoverPolicyAbilities(project) {
|
|
|
80
130
|
}
|
|
81
131
|
return found;
|
|
82
132
|
}
|
|
83
|
-
/** `@
|
|
84
|
-
function resourceNameFromPolicy(
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
133
|
+
/** `@PolicyFor(Post)` → `'Post'`; `undefined` without the link (the official `@Policy()` alone). */
|
|
134
|
+
function resourceNameFromPolicy(classDecl) {
|
|
135
|
+
for (const decorator of classDecl.getDecorators()) {
|
|
136
|
+
if (decorator.getName() !== 'PolicyFor')
|
|
137
|
+
continue;
|
|
138
|
+
const arg = decorator.getArguments()[0];
|
|
139
|
+
if (arg && Node.isIdentifier(arg))
|
|
140
|
+
return arg.getText();
|
|
141
|
+
}
|
|
91
142
|
return undefined;
|
|
92
143
|
}
|
|
93
144
|
/** The string-literal union of all discovered abilities, e.g. `"update" | "create"` (or `never`). */
|
|
@@ -106,8 +157,8 @@ function resourceAbilityMapLiteral(byResource) {
|
|
|
106
157
|
/** The `import { ... } from '@dudousxd/nestjs-authz-client'` line the header contributes. */
|
|
107
158
|
function canHelperImports() {
|
|
108
159
|
return [
|
|
109
|
-
"import { AbilityStore, createCan } from '@dudousxd/nestjs-authz-client';",
|
|
110
|
-
"import type { ResourceRef } from '@dudousxd/nestjs-authz-client';",
|
|
160
|
+
"import { AbilityStore, createCan, createCanBatch } from '@dudousxd/nestjs-authz-client';",
|
|
161
|
+
"import type { ResourceRef, BatchAbilityResult } from '@dudousxd/nestjs-authz-client';",
|
|
111
162
|
];
|
|
112
163
|
}
|
|
113
164
|
/**
|
|
@@ -160,14 +211,37 @@ function canHelperStatements(union, mapLiteral, endpoint) {
|
|
|
160
211
|
'): boolean | Promise<boolean> {',
|
|
161
212
|
' return authzCan(ability, resource ?? undefined);',
|
|
162
213
|
'}',
|
|
214
|
+
'',
|
|
215
|
+
'/**',
|
|
216
|
+
" * Batch resolver from '@dudousxd/nestjs-authz-client', bound to `authzStore`. For list",
|
|
217
|
+
' * pages: answers hydrated items from the store and POSTs ALL cache-misses to',
|
|
218
|
+
` * \`${endpoint}\` in ONE request (instead of one per item).`,
|
|
219
|
+
' */',
|
|
220
|
+
'const authzCanBatch = createCanBatch(authzStore, {',
|
|
221
|
+
` fallback: 'fetch',`,
|
|
222
|
+
` endpoint: ${JSON.stringify(endpoint)},`,
|
|
223
|
+
'});',
|
|
224
|
+
'',
|
|
225
|
+
'/**',
|
|
226
|
+
' * Authorize MANY abilities at once. Each item is typed against the discovered',
|
|
227
|
+
' * abilities, so a wrong ability is a compile error. Hydrated decisions resolve with',
|
|
228
|
+
` * no request; the remaining misses are fetched from \`${endpoint}\` in a single batch.`,
|
|
229
|
+
' */',
|
|
230
|
+
'export function canBatch(',
|
|
231
|
+
' items: ReadonlyArray<{ ability: AuthzAbility; resource?: ResourceRef | null }>,',
|
|
232
|
+
'): Promise<BatchAbilityResult[]> {',
|
|
233
|
+
' return authzCanBatch(',
|
|
234
|
+
' items.map((i) => (i.resource == null ? { ability: i.ability } : { ability: i.ability, resource: i.resource })),',
|
|
235
|
+
' );',
|
|
236
|
+
'}',
|
|
163
237
|
];
|
|
164
238
|
}
|
|
165
239
|
function markerOf(route) {
|
|
166
240
|
return route.contract?.contractSource?.authz;
|
|
167
241
|
}
|
|
168
242
|
/**
|
|
169
|
-
* `@dudousxd/nestjs-authz` codegen extension. Discovers every `@Can(ability,
|
|
170
|
-
* on your controllers (and, optionally, `@Policy`
|
|
243
|
+
* `@dudousxd/nestjs-authz` codegen extension. Discovers every official `@Can(Policy, 'ability', ...)`
|
|
244
|
+
* (and `@Permission`) on your controllers (and, optionally, the methods of `@Policy()` classes) and emits a typed `can()` helper
|
|
171
245
|
* into your generated `api.ts`: `ability` is a string-literal union of the discovered abilities, so a
|
|
172
246
|
* wrong ability is a compile error. The runtime `can()` delegates to `@dudousxd/nestjs-authz-client`
|
|
173
247
|
* (a shared `AbilityStore` + `createCan`) — so a hydrated decision answers with no request, and a
|
|
@@ -201,10 +275,12 @@ export function nestjsAuthzCodegen(options = {}) {
|
|
|
201
275
|
transformRoutes(routes, ctx) {
|
|
202
276
|
const project = ctx.project();
|
|
203
277
|
const out = routes.map((route) => {
|
|
204
|
-
const
|
|
278
|
+
const found = discoverRouteAbilities(route, project);
|
|
279
|
+
for (const each of found)
|
|
280
|
+
record(each);
|
|
281
|
+
const info = found[0];
|
|
205
282
|
if (!info)
|
|
206
283
|
return route;
|
|
207
|
-
record(info);
|
|
208
284
|
const marker = { ability: info.ability };
|
|
209
285
|
if (info.resource)
|
|
210
286
|
marker.resource = info.resource;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAyB,IAAI,EAAiC,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dudousxd/nestjs-authz-codegen",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "nestjs-codegen extension for @dudousxd/nestjs-authz — discovers @Can abilities and emits a typed can() into your generated client.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -8,6 +8,19 @@
|
|
|
8
8
|
"url": "git+https://github.com/DavideCarvalho/nestjs-authz.git",
|
|
9
9
|
"directory": "packages/codegen"
|
|
10
10
|
},
|
|
11
|
+
"homepage": "https://davidecarvalho.github.io/aviary/docs/authz",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/DavideCarvalho/nestjs-authz/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"nestjs",
|
|
17
|
+
"authorization",
|
|
18
|
+
"authz",
|
|
19
|
+
"codegen",
|
|
20
|
+
"extension",
|
|
21
|
+
"can",
|
|
22
|
+
"policy"
|
|
23
|
+
],
|
|
11
24
|
"author": "Davi Carvalho <davi@goflip.ai>",
|
|
12
25
|
"type": "module",
|
|
13
26
|
"main": "./dist/index.js",
|
|
@@ -25,7 +38,7 @@
|
|
|
25
38
|
"CHANGELOG.md"
|
|
26
39
|
],
|
|
27
40
|
"peerDependencies": {
|
|
28
|
-
"@dudousxd/nestjs-authz": ">=0.
|
|
41
|
+
"@dudousxd/nestjs-authz": ">=0.9.0 <0.10.0",
|
|
29
42
|
"@dudousxd/nestjs-authz-client": ">=0.0.0",
|
|
30
43
|
"@dudousxd/nestjs-codegen": ">=0.4.0"
|
|
31
44
|
},
|
|
@@ -35,27 +48,18 @@
|
|
|
35
48
|
}
|
|
36
49
|
},
|
|
37
50
|
"devDependencies": {
|
|
38
|
-
"@dudousxd/nestjs-codegen": "0.
|
|
39
|
-
"ts-morph": "
|
|
40
|
-
"typescript": "
|
|
41
|
-
"vitest": "
|
|
42
|
-
"@dudousxd/nestjs-authz": "^0.
|
|
43
|
-
"@dudousxd/nestjs-authz
|
|
51
|
+
"@dudousxd/nestjs-codegen": "0.25.1",
|
|
52
|
+
"ts-morph": "28.0.0",
|
|
53
|
+
"typescript": "5.9.3",
|
|
54
|
+
"vitest": "3.2.7",
|
|
55
|
+
"@dudousxd/nestjs-authz-client": "^0.2.2",
|
|
56
|
+
"@dudousxd/nestjs-authz": "^0.9.1"
|
|
44
57
|
},
|
|
45
58
|
"engines": {
|
|
46
|
-
"node": ">=
|
|
59
|
+
"node": ">=22"
|
|
47
60
|
},
|
|
48
|
-
"keywords": [
|
|
49
|
-
"nestjs",
|
|
50
|
-
"authorization",
|
|
51
|
-
"authz",
|
|
52
|
-
"codegen",
|
|
53
|
-
"extension",
|
|
54
|
-
"can",
|
|
55
|
-
"policy"
|
|
56
|
-
],
|
|
57
61
|
"scripts": {
|
|
58
|
-
"build": "tsc -p tsconfig.json",
|
|
62
|
+
"build": "tsc -p tsconfig.json --incremental false",
|
|
59
63
|
"test": "vitest run --passWithNoTests",
|
|
60
64
|
"test:watch": "vitest",
|
|
61
65
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|