@dudousxd/nestjs-authz-typeorm 0.2.1 → 0.3.1
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 +64 -0
- package/dist/authz-rbac.module.d.ts +1 -1
- package/dist/authz-rbac.module.d.ts.map +1 -1
- package/dist/authz-rbac.module.js +86 -8
- package/dist/authz-rbac.module.js.map +1 -1
- package/dist/entities.d.ts +30 -1
- package/dist/entities.d.ts.map +1 -1
- package/dist/entities.js +51 -0
- package/dist/entities.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +28 -4
- package/dist/schema.js.map +1 -1
- package/dist/scope.d.ts +75 -0
- package/dist/scope.d.ts.map +1 -0
- package/dist/scope.js +124 -0
- package/dist/scope.js.map +1 -0
- package/dist/sql.d.ts +1 -8
- package/dist/sql.d.ts.map +1 -1
- package/dist/sql.js +3 -19
- package/dist/sql.js.map +1 -1
- package/dist/typeorm-authz.store.d.ts +59 -21
- package/dist/typeorm-authz.store.d.ts.map +1 -1
- package/dist/typeorm-authz.store.js +129 -33
- package/dist/typeorm-authz.store.js.map +1 -1
- package/dist/types.d.ts +10 -9
- package/dist/types.d.ts.map +1 -1
- package/package.json +9 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,69 @@
|
|
|
1
1
|
# @dudousxd/nestjs-authz-typeorm
|
|
2
2
|
|
|
3
|
+
## 0.3.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`61b6b92`](https://github.com/DavideCarvalho/nestjs-authz/commit/61b6b9241438b8f30811fcd17b0c0c98f08af3bf) - Internal refactors (behavior-preserving): single-source the ORM store contract (`UserRef`/`UserRefInput`/`UserAuthz`/`normalizeUserRef`) via a new `@dudousxd/nestjs-authz/store-kit` subpath that the typeorm/prisma/mikro-orm adapters re-export under their public names, so the definition can't drift across them. Also single-source the grant preamble and the SQL identifier guard in the core store path.
|
|
8
|
+
|
|
9
|
+
## 0.3.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [#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.
|
|
14
|
+
|
|
15
|
+
### Permissions
|
|
16
|
+
|
|
17
|
+
- **Wildcard / hierarchical permissions.** Permission strings now support wildcard
|
|
18
|
+
and hierarchical matching (e.g. `posts:*` or `posts:read:*`), so a single granted
|
|
19
|
+
permission can authorize a family of related actions instead of enumerating each one.
|
|
20
|
+
|
|
21
|
+
### Authorization decisions
|
|
22
|
+
|
|
23
|
+
- **Deny reason / message surfaced on `ForbiddenException`.** When a check fails, the
|
|
24
|
+
reason for the denial is propagated onto the thrown `ForbiddenException`, making it
|
|
25
|
+
possible to return a meaningful message to the caller and to debug authorization
|
|
26
|
+
failures.
|
|
27
|
+
- **Gate `after` hook.** The Gate now exposes an `after` hook that runs once a decision
|
|
28
|
+
has been made, enabling cross-cutting concerns such as auditing, logging, and metrics.
|
|
29
|
+
|
|
30
|
+
### Batch authorization
|
|
31
|
+
|
|
32
|
+
- **`allowsMany`** for evaluating multiple permission checks in a single call.
|
|
33
|
+
- **Batch `/authz/can` endpoint** so clients can resolve many checks in one round trip.
|
|
34
|
+
- **`createCanBatch`** client helper that batches `can` calls transparently.
|
|
35
|
+
|
|
36
|
+
### Performance
|
|
37
|
+
|
|
38
|
+
- **Request-scoped permission cache.** Permissions resolved during a request are cached
|
|
39
|
+
for the lifetime of that request, avoiding repeated lookups on the hot path.
|
|
40
|
+
|
|
41
|
+
### RBAC adapters (TypeORM)
|
|
42
|
+
|
|
43
|
+
- **Direct user permissions** granted to a user independent of their roles.
|
|
44
|
+
- **Tenant-scoped roles** so the same role can be assigned per tenant in multi-tenant
|
|
45
|
+
deployments.
|
|
46
|
+
|
|
47
|
+
### Query scoping / policy filter (ABAC)
|
|
48
|
+
|
|
49
|
+
- **ORM-neutral constraint AST.** Policies can produce a portable constraint
|
|
50
|
+
representation describing which rows a subject may access.
|
|
51
|
+
- **Per-ORM application.** The constraint AST is translated and applied for
|
|
52
|
+
**TypeORM**, **MikroORM**, and **Prisma**, giving ABAC-style query scoping that
|
|
53
|
+
filters data at the database layer regardless of the ORM in use.
|
|
54
|
+
|
|
55
|
+
### Testing
|
|
56
|
+
|
|
57
|
+
- **New `@dudousxd/nestjs-authz-testing` package** with fakes and helpers for testing
|
|
58
|
+
authorization in consumer applications.
|
|
59
|
+
- **Postgres / MySQL testcontainers + contract tests** so the ORM stores are verified
|
|
60
|
+
against real database engines, and a shared contract suite keeps the adapters
|
|
61
|
+
behaviorally consistent.
|
|
62
|
+
|
|
63
|
+
### Housekeeping
|
|
64
|
+
|
|
65
|
+
- **Packaging hygiene** across the published packages.
|
|
66
|
+
|
|
3
67
|
## 0.2.1
|
|
4
68
|
|
|
5
69
|
### Patch Changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import type { UserRef } from '@dudousxd/nestjs-authz/store-kit';
|
|
1
2
|
import { type DynamicModule } from '@nestjs/common';
|
|
2
3
|
import type { TypeOrmAuthzStore } from './typeorm-authz.store.js';
|
|
3
|
-
import type { UserRef } from './types.js';
|
|
4
4
|
/** Injection token holding the {@link TypeOrmAuthzStore} the RBAC module manages. */
|
|
5
5
|
export declare const AUTHZ_RBAC_STORE: unique symbol;
|
|
6
6
|
/** Injection token holding the resolved {@link AuthzRbacModuleOptions}. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authz-rbac.module.d.ts","sourceRoot":"","sources":["../src/authz-rbac.module.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"authz-rbac.module.d.ts","sourceRoot":"","sources":["../src/authz-rbac.module.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAChE,OAAO,EACL,KAAK,aAAa,EAQnB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,qFAAqF;AACrF,eAAO,MAAM,gBAAgB,eAAqD,CAAC;AACnF,2EAA2E;AAC3E,eAAO,MAAM,kBAAkB,eAAuD,CAAC;AAEvF;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,GAAG,SAAS,CAAC;AAEnE,MAAM,WAAW,sBAAsB;IACrC,uEAAuE;IACvE,KAAK,EAAE,iBAAiB,CAAC;IACzB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,WAAW,CAAC,EAAE,aAAa,CAAC;CAC7B;AAED,MAAM,WAAW,2BAA2B;IAC1C,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,UAAU,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,sBAAsB,CAAC,GAAG,sBAAsB,CAAC;CAC9F;AAgDD,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CAOvE;AA+FD;;;;;;GAMG;AACH,qBACa,eAAe;IAC1B,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,sBAAsB,GAAG,aAAa;IAa9D,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,2BAA2B,GAAG,aAAa;IAsBxE,OAAO,CAAC,MAAM,CAAC,eAAe;CAS/B"}
|
|
@@ -11,12 +11,49 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
11
11
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
12
|
};
|
|
13
13
|
var AuthzRbacModule_1;
|
|
14
|
-
import { PERMISSION_PROVIDER, ROLE_PROVIDER, } from '@dudousxd/nestjs-authz';
|
|
14
|
+
import { CONTEXT_ACCESSOR, PERMISSION_PROVIDER, ROLE_PROVIDER, } from '@dudousxd/nestjs-authz';
|
|
15
15
|
import { Inject, Injectable, Module, Optional, } from '@nestjs/common';
|
|
16
|
+
import { ModuleRef } from '@nestjs/core';
|
|
16
17
|
/** Injection token holding the {@link TypeOrmAuthzStore} the RBAC module manages. */
|
|
17
18
|
export const AUTHZ_RBAC_STORE = Symbol.for('@dudousxd/nestjs-authz-typeorm:store');
|
|
18
19
|
/** Injection token holding the resolved {@link AuthzRbacModuleOptions}. */
|
|
19
20
|
export const AUTHZ_RBAC_OPTIONS = Symbol.for('@dudousxd/nestjs-authz-typeorm:options');
|
|
21
|
+
/** Read the current tenant from the (optional) context accessor, defensively. */
|
|
22
|
+
function tenantScopeFrom(context) {
|
|
23
|
+
if (!context || typeof context.tenantId !== 'function')
|
|
24
|
+
return {};
|
|
25
|
+
let tenantId;
|
|
26
|
+
try {
|
|
27
|
+
tenantId = context.tenantId();
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
tenantId = undefined;
|
|
31
|
+
}
|
|
32
|
+
return tenantId == null ? {} : { tenantId };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Locate the context accessor for tenant scoping. Prefers a value injected into
|
|
36
|
+
* this module; falls back to a non-strict {@link ModuleRef} lookup so an accessor
|
|
37
|
+
* provided by ANY module (the app root, a global ContextModule) is still found —
|
|
38
|
+
* the RBAC module is global and its providers don't import the app's modules, so a
|
|
39
|
+
* plain `@Inject` would miss a root-provided accessor. Mirrors the Gate's seam.
|
|
40
|
+
*/
|
|
41
|
+
function resolveTenantContext(injected, moduleRef, cache) {
|
|
42
|
+
if (injected)
|
|
43
|
+
return injected;
|
|
44
|
+
if (cache.resolved)
|
|
45
|
+
return cache.value;
|
|
46
|
+
cache.resolved = true;
|
|
47
|
+
if (moduleRef) {
|
|
48
|
+
try {
|
|
49
|
+
cache.value = moduleRef.get(CONTEXT_ACCESSOR, { strict: false });
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
cache.value = undefined;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return cache.value;
|
|
56
|
+
}
|
|
20
57
|
/** Default mapping: accept a `{ type, id }` ref, a `{ id }` object, or a bare id. */
|
|
21
58
|
export function defaultUserRefMapper(user) {
|
|
22
59
|
if (user == null)
|
|
@@ -36,25 +73,52 @@ export function defaultUserRefMapper(user) {
|
|
|
36
73
|
*/
|
|
37
74
|
let RbacPermissionProvider = class RbacPermissionProvider {
|
|
38
75
|
options;
|
|
39
|
-
|
|
76
|
+
context;
|
|
77
|
+
moduleRef;
|
|
78
|
+
contextCache = {
|
|
79
|
+
resolved: false,
|
|
80
|
+
value: undefined,
|
|
81
|
+
};
|
|
82
|
+
constructor(options, context, moduleRef) {
|
|
40
83
|
this.options = options;
|
|
84
|
+
this.context = context;
|
|
85
|
+
this.moduleRef = moduleRef;
|
|
86
|
+
}
|
|
87
|
+
scope() {
|
|
88
|
+
return tenantScopeFrom(resolveTenantContext(this.context, this.moduleRef, this.contextCache));
|
|
41
89
|
}
|
|
42
90
|
// The core `PermissionProvider` interface passes an optional `resource` (3rd
|
|
43
91
|
// arg); it is intentionally ignored here. RBAC grants are model-less,
|
|
44
92
|
// named-ability grants (the Laravel/spatie `Gate::before` grant), so the
|
|
45
|
-
// verdict never depends on a specific resource instance.
|
|
93
|
+
// verdict never depends on a specific resource instance. Permissions are scoped
|
|
94
|
+
// by the current tenant (from nestjs-context, when present) — a tenant-scoped
|
|
95
|
+
// role only grants within its tenant; direct user grants always apply.
|
|
46
96
|
async hasPermission(user, permission) {
|
|
47
97
|
const map = this.options.userRefFrom ?? defaultUserRefMapper;
|
|
48
98
|
const ref = map(user);
|
|
49
99
|
if (ref === undefined)
|
|
50
100
|
return undefined;
|
|
51
|
-
return this.options.store.userHasPermission(ref, permission);
|
|
101
|
+
return this.options.store.userHasPermission(ref, permission, this.scope());
|
|
102
|
+
}
|
|
103
|
+
// Expose the user's full granted permission set so the core can apply
|
|
104
|
+
// Laravel/spatie-style wildcard matching (a granted `posts.*` satisfies a check
|
|
105
|
+
// for `posts.update`, `*` satisfies anything). The matching itself lives in core,
|
|
106
|
+
// so every adapter that lists grants gets wildcard semantics for free.
|
|
107
|
+
async getPermissions(user) {
|
|
108
|
+
const map = this.options.userRefFrom ?? defaultUserRefMapper;
|
|
109
|
+
const ref = map(user);
|
|
110
|
+
if (ref === undefined)
|
|
111
|
+
return undefined;
|
|
112
|
+
return this.options.store.getPermissionsForUser(ref, this.scope());
|
|
52
113
|
}
|
|
53
114
|
};
|
|
54
115
|
RbacPermissionProvider = __decorate([
|
|
55
116
|
Injectable(),
|
|
56
117
|
__param(0, Inject(AUTHZ_RBAC_OPTIONS)),
|
|
57
|
-
|
|
118
|
+
__param(1, Optional()),
|
|
119
|
+
__param(1, Inject(CONTEXT_ACCESSOR)),
|
|
120
|
+
__param(2, Optional()),
|
|
121
|
+
__metadata("design:paramtypes", [Object, Object, ModuleRef])
|
|
58
122
|
], RbacPermissionProvider);
|
|
59
123
|
/**
|
|
60
124
|
* The RBAC {@link RoleProvider} the Gate consults (via the shared {@link ROLE_PROVIDER}
|
|
@@ -64,21 +128,35 @@ RbacPermissionProvider = __decorate([
|
|
|
64
128
|
*/
|
|
65
129
|
let RbacRoleProvider = class RbacRoleProvider {
|
|
66
130
|
options;
|
|
67
|
-
|
|
131
|
+
context;
|
|
132
|
+
moduleRef;
|
|
133
|
+
contextCache = {
|
|
134
|
+
resolved: false,
|
|
135
|
+
value: undefined,
|
|
136
|
+
};
|
|
137
|
+
constructor(options, context, moduleRef) {
|
|
68
138
|
this.options = options;
|
|
139
|
+
this.context = context;
|
|
140
|
+
this.moduleRef = moduleRef;
|
|
69
141
|
}
|
|
70
142
|
async getRoles(user) {
|
|
71
143
|
const map = this.options.userRefFrom ?? defaultUserRefMapper;
|
|
72
144
|
const ref = map(user);
|
|
73
145
|
if (ref === undefined)
|
|
74
146
|
return undefined;
|
|
75
|
-
|
|
147
|
+
// Scope roles by the current tenant (when nestjs-context is present): a
|
|
148
|
+
// tenant-scoped role only counts within its tenant; global roles always count.
|
|
149
|
+
const scope = tenantScopeFrom(resolveTenantContext(this.context, this.moduleRef, this.contextCache));
|
|
150
|
+
return this.options.store.getRolesForUser(ref, scope);
|
|
76
151
|
}
|
|
77
152
|
};
|
|
78
153
|
RbacRoleProvider = __decorate([
|
|
79
154
|
Injectable(),
|
|
80
155
|
__param(0, Inject(AUTHZ_RBAC_OPTIONS)),
|
|
81
|
-
|
|
156
|
+
__param(1, Optional()),
|
|
157
|
+
__param(1, Inject(CONTEXT_ACCESSOR)),
|
|
158
|
+
__param(2, Optional()),
|
|
159
|
+
__metadata("design:paramtypes", [Object, Object, ModuleRef])
|
|
82
160
|
], RbacRoleProvider);
|
|
83
161
|
/** Runs `ensureAuthzSchema` on bootstrap when `autoCreateSchema` is not disabled. */
|
|
84
162
|
let AuthzRbacBootstrap = class AuthzRbacBootstrap {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authz-rbac.module.js","sourceRoot":"","sources":["../src/authz-rbac.module.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,OAAO,EACL,mBAAmB,EAEnB,aAAa,GAEd,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"authz-rbac.module.js","sourceRoot":"","sources":["../src/authz-rbac.module.ts"],"names":[],"mappings":"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"}
|
package/dist/entities.d.ts
CHANGED
|
@@ -9,7 +9,17 @@ export declare const DEFAULT_TABLE_NAMES: {
|
|
|
9
9
|
readonly permissions: "authz_permissions";
|
|
10
10
|
readonly rolePermission: "authz_role_permission";
|
|
11
11
|
readonly userRole: "authz_user_role";
|
|
12
|
+
readonly userPermission: "authz_user_permission";
|
|
12
13
|
};
|
|
14
|
+
/**
|
|
15
|
+
* The "no tenant" sentinel for the user-role pivot's `tenantId`. A GLOBAL (unscoped)
|
|
16
|
+
* assignment stores this empty string rather than `NULL` so the column stays part of
|
|
17
|
+
* the composite primary key with portable, dialect-correct uniqueness (SQL treats
|
|
18
|
+
* `NULL`s as distinct, which would break idempotency of a global grant and silently
|
|
19
|
+
* permit duplicates). The column is added post-v1 with a default of this value, so an
|
|
20
|
+
* older populated table self-heals via `ensureAuthzSchema` (`ADD COLUMN ... DEFAULT ''`).
|
|
21
|
+
*/
|
|
22
|
+
export declare const GLOBAL_TENANT = "";
|
|
13
23
|
/**
|
|
14
24
|
* A named role (e.g. `editor`). Permissions are attached via {@link RolePermissionEntity};
|
|
15
25
|
* users are attached via {@link UserRoleEntity}.
|
|
@@ -40,12 +50,31 @@ export declare class RolePermissionEntity {
|
|
|
40
50
|
* Pivot: user ↔ role. References the user BY ID ONLY — this package NEVER defines or
|
|
41
51
|
* owns a users table. `userType` lets the same table key polymorphic principals
|
|
42
52
|
* (mirrors nestjs-context's `UserRef` shape).
|
|
53
|
+
*
|
|
54
|
+
* `tenantId` (post-v1, defaulted to {@link GLOBAL_TENANT} = `''`) scopes the
|
|
55
|
+
* assignment to a tenant: an empty value = global (applies in every tenant), a
|
|
56
|
+
* non-empty value = only within that tenant. It is part of the composite PK so the
|
|
57
|
+
* same `(user, role)` can be granted independently in multiple tenants while staying
|
|
58
|
+
* idempotent per tenant. Defaulted (not nullable) so the column belongs to the PK
|
|
59
|
+
* with portable uniqueness — see {@link GLOBAL_TENANT}.
|
|
43
60
|
*/
|
|
44
61
|
export declare class UserRoleEntity {
|
|
45
62
|
userType: string;
|
|
46
63
|
userId: string;
|
|
47
64
|
roleId: string;
|
|
65
|
+
tenantId: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Pivot: user ↔ permission (a DIRECT grant, no role needed — Laravel/spatie's
|
|
69
|
+
* `$user->givePermissionTo(...)`). Mirrors {@link UserRoleEntity}: the user is
|
|
70
|
+
* referenced BY ID ONLY and `userType` keys polymorphic principals. Composite PK
|
|
71
|
+
* `(userType, userId, permissionId)`.
|
|
72
|
+
*/
|
|
73
|
+
export declare class UserPermissionEntity {
|
|
74
|
+
userType: string;
|
|
75
|
+
userId: string;
|
|
76
|
+
permissionId: string;
|
|
48
77
|
}
|
|
49
78
|
/** All entities, in dependency order — convenient for `entities: [...]` registration. */
|
|
50
|
-
export declare const AUTHZ_ENTITIES: readonly [typeof RoleEntity, typeof PermissionEntity, typeof RolePermissionEntity, typeof UserRoleEntity];
|
|
79
|
+
export declare const AUTHZ_ENTITIES: readonly [typeof RoleEntity, typeof PermissionEntity, typeof RolePermissionEntity, typeof UserRoleEntity, typeof UserPermissionEntity];
|
|
51
80
|
//# sourceMappingURL=entities.d.ts.map
|
package/dist/entities.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entities.d.ts","sourceRoot":"","sources":["../src/entities.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB
|
|
1
|
+
{"version":3,"file":"entities.d.ts","sourceRoot":"","sources":["../src/entities.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB;;;;;;CAMtB,CAAC;AAEX;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC;;;;;;;GAOG;AACH,qBACa,UAAU;IAErB,EAAE,EAAG,MAAM,CAAC;IAIZ,IAAI,EAAG,MAAM,CAAC;IAGd,KAAK,EAAG,MAAM,GAAG,IAAI,CAAC;IAGtB,SAAS,EAAG,IAAI,CAAC;CAClB;AAED,oGAAoG;AACpG,qBACa,gBAAgB;IAE3B,EAAE,EAAG,MAAM,CAAC;IAIZ,IAAI,EAAG,MAAM,CAAC;IAGd,KAAK,EAAG,MAAM,GAAG,IAAI,CAAC;IAGtB,SAAS,EAAG,IAAI,CAAC;CAClB;AAED,uEAAuE;AACvE,qBACa,oBAAoB;IAE/B,MAAM,EAAG,MAAM,CAAC;IAGhB,YAAY,EAAG,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;GAWG;AACH,qBAEa,cAAc;IAEzB,QAAQ,EAAG,MAAM,CAAC;IAGlB,MAAM,EAAG,MAAM,CAAC;IAGhB,MAAM,EAAG,MAAM,CAAC;IAGhB,QAAQ,EAAG,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,qBAEa,oBAAoB;IAE/B,QAAQ,EAAG,MAAM,CAAC;IAGlB,MAAM,EAAG,MAAM,CAAC;IAGhB,YAAY,EAAG,MAAM,CAAC;CACvB;AAED,yFAAyF;AACzF,eAAO,MAAM,cAAc,wIAMjB,CAAC"}
|
package/dist/entities.js
CHANGED
|
@@ -19,7 +19,17 @@ export const DEFAULT_TABLE_NAMES = {
|
|
|
19
19
|
permissions: 'authz_permissions',
|
|
20
20
|
rolePermission: 'authz_role_permission',
|
|
21
21
|
userRole: 'authz_user_role',
|
|
22
|
+
userPermission: 'authz_user_permission',
|
|
22
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* The "no tenant" sentinel for the user-role pivot's `tenantId`. A GLOBAL (unscoped)
|
|
26
|
+
* assignment stores this empty string rather than `NULL` so the column stays part of
|
|
27
|
+
* the composite primary key with portable, dialect-correct uniqueness (SQL treats
|
|
28
|
+
* `NULL`s as distinct, which would break idempotency of a global grant and silently
|
|
29
|
+
* permit duplicates). The column is added post-v1 with a default of this value, so an
|
|
30
|
+
* older populated table self-heals via `ensureAuthzSchema` (`ADD COLUMN ... DEFAULT ''`).
|
|
31
|
+
*/
|
|
32
|
+
export const GLOBAL_TENANT = '';
|
|
23
33
|
/**
|
|
24
34
|
* A named role (e.g. `editor`). Permissions are attached via {@link RolePermissionEntity};
|
|
25
35
|
* users are attached via {@link UserRoleEntity}.
|
|
@@ -104,11 +114,19 @@ export { RolePermissionEntity };
|
|
|
104
114
|
* Pivot: user ↔ role. References the user BY ID ONLY — this package NEVER defines or
|
|
105
115
|
* owns a users table. `userType` lets the same table key polymorphic principals
|
|
106
116
|
* (mirrors nestjs-context's `UserRef` shape).
|
|
117
|
+
*
|
|
118
|
+
* `tenantId` (post-v1, defaulted to {@link GLOBAL_TENANT} = `''`) scopes the
|
|
119
|
+
* assignment to a tenant: an empty value = global (applies in every tenant), a
|
|
120
|
+
* non-empty value = only within that tenant. It is part of the composite PK so the
|
|
121
|
+
* same `(user, role)` can be granted independently in multiple tenants while staying
|
|
122
|
+
* idempotent per tenant. Defaulted (not nullable) so the column belongs to the PK
|
|
123
|
+
* with portable uniqueness — see {@link GLOBAL_TENANT}.
|
|
107
124
|
*/
|
|
108
125
|
let UserRoleEntity = class UserRoleEntity {
|
|
109
126
|
userType;
|
|
110
127
|
userId;
|
|
111
128
|
roleId;
|
|
129
|
+
tenantId;
|
|
112
130
|
};
|
|
113
131
|
__decorate([
|
|
114
132
|
PrimaryColumn({ type: 'varchar', length: 191 }),
|
|
@@ -122,16 +140,49 @@ __decorate([
|
|
|
122
140
|
PrimaryColumn({ type: 'varchar', length: 191 }),
|
|
123
141
|
__metadata("design:type", String)
|
|
124
142
|
], UserRoleEntity.prototype, "roleId", void 0);
|
|
143
|
+
__decorate([
|
|
144
|
+
PrimaryColumn({ type: 'varchar', length: 191, default: GLOBAL_TENANT }),
|
|
145
|
+
__metadata("design:type", String)
|
|
146
|
+
], UserRoleEntity.prototype, "tenantId", void 0);
|
|
125
147
|
UserRoleEntity = __decorate([
|
|
126
148
|
Entity({ name: DEFAULT_TABLE_NAMES.userRole }),
|
|
127
149
|
Index(['userType', 'userId'])
|
|
128
150
|
], UserRoleEntity);
|
|
129
151
|
export { UserRoleEntity };
|
|
152
|
+
/**
|
|
153
|
+
* Pivot: user ↔ permission (a DIRECT grant, no role needed — Laravel/spatie's
|
|
154
|
+
* `$user->givePermissionTo(...)`). Mirrors {@link UserRoleEntity}: the user is
|
|
155
|
+
* referenced BY ID ONLY and `userType` keys polymorphic principals. Composite PK
|
|
156
|
+
* `(userType, userId, permissionId)`.
|
|
157
|
+
*/
|
|
158
|
+
let UserPermissionEntity = class UserPermissionEntity {
|
|
159
|
+
userType;
|
|
160
|
+
userId;
|
|
161
|
+
permissionId;
|
|
162
|
+
};
|
|
163
|
+
__decorate([
|
|
164
|
+
PrimaryColumn({ type: 'varchar', length: 191 }),
|
|
165
|
+
__metadata("design:type", String)
|
|
166
|
+
], UserPermissionEntity.prototype, "userType", void 0);
|
|
167
|
+
__decorate([
|
|
168
|
+
PrimaryColumn({ type: 'varchar', length: 191 }),
|
|
169
|
+
__metadata("design:type", String)
|
|
170
|
+
], UserPermissionEntity.prototype, "userId", void 0);
|
|
171
|
+
__decorate([
|
|
172
|
+
PrimaryColumn({ type: 'varchar', length: 191 }),
|
|
173
|
+
__metadata("design:type", String)
|
|
174
|
+
], UserPermissionEntity.prototype, "permissionId", void 0);
|
|
175
|
+
UserPermissionEntity = __decorate([
|
|
176
|
+
Entity({ name: DEFAULT_TABLE_NAMES.userPermission }),
|
|
177
|
+
Index(['userType', 'userId'])
|
|
178
|
+
], UserPermissionEntity);
|
|
179
|
+
export { UserPermissionEntity };
|
|
130
180
|
/** All entities, in dependency order — convenient for `entities: [...]` registration. */
|
|
131
181
|
export const AUTHZ_ENTITIES = [
|
|
132
182
|
RoleEntity,
|
|
133
183
|
PermissionEntity,
|
|
134
184
|
RolePermissionEntity,
|
|
135
185
|
UserRoleEntity,
|
|
186
|
+
UserPermissionEntity,
|
|
136
187
|
];
|
|
137
188
|
//# sourceMappingURL=entities.js.map
|
package/dist/entities.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entities.js","sourceRoot":"","sources":["../src/entities.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,KAAK,EAAE,aAAa;IACpB,WAAW,EAAE,mBAAmB;IAChC,cAAc,EAAE,uBAAuB;IACvC,QAAQ,EAAE,iBAAiB;
|
|
1
|
+
{"version":3,"file":"entities.js","sourceRoot":"","sources":["../src/entities.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
export { AUTHZ_ENTITIES, DEFAULT_TABLE_NAMES, PermissionEntity, RoleEntity, RolePermissionEntity, UserRoleEntity, } from './entities.js';
|
|
1
|
+
export { AUTHZ_ENTITIES, DEFAULT_TABLE_NAMES, GLOBAL_TENANT, PermissionEntity, RoleEntity, RolePermissionEntity, UserPermissionEntity, UserRoleEntity, } from './entities.js';
|
|
2
2
|
export { createAuthzTables, ensureAuthzSchema } from './schema.js';
|
|
3
3
|
export { TypeOrmAuthzStore } from './typeorm-authz.store.js';
|
|
4
4
|
export type { UserAuthz } from './typeorm-authz.store.js';
|
|
5
5
|
export { AUTHZ_RBAC_OPTIONS, AUTHZ_RBAC_STORE, AuthzRbacModule, defaultUserRefMapper, } from './authz-rbac.module.js';
|
|
6
6
|
export type { AuthzRbacModuleAsyncOptions, AuthzRbacModuleOptions, UserRefMapper, } from './authz-rbac.module.js';
|
|
7
|
-
export type { AuthzStoreOptions, TableNames,
|
|
7
|
+
export type { AuthzStoreOptions, TableNames, TenantScope } from './types.js';
|
|
8
|
+
export type { UserRef, UserRefInput } from '@dudousxd/nestjs-authz/store-kit';
|
|
9
|
+
export { applyScope, applyScopeConstraint, compileScope } from './scope.js';
|
|
10
|
+
export type { CompiledScope, ScopeResolver } from './scope.js';
|
|
8
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,gBAAgB,EAChB,UAAU,EACV,oBAAoB,EACpB,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,2BAA2B,EAC3B,sBAAsB,EACtB,aAAa,GACd,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,2BAA2B,EAC3B,sBAAsB,EACtB,aAAa,GACd,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC7E,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAC9E,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC5E,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export { AUTHZ_ENTITIES, DEFAULT_TABLE_NAMES, PermissionEntity, RoleEntity, RolePermissionEntity, UserRoleEntity, } from './entities.js';
|
|
1
|
+
export { AUTHZ_ENTITIES, DEFAULT_TABLE_NAMES, GLOBAL_TENANT, PermissionEntity, RoleEntity, RolePermissionEntity, UserPermissionEntity, UserRoleEntity, } from './entities.js';
|
|
2
2
|
export { createAuthzTables, ensureAuthzSchema } from './schema.js';
|
|
3
3
|
export { TypeOrmAuthzStore } from './typeorm-authz.store.js';
|
|
4
4
|
export { AUTHZ_RBAC_OPTIONS, AUTHZ_RBAC_STORE, AuthzRbacModule, defaultUserRefMapper, } from './authz-rbac.module.js';
|
|
5
|
+
export { applyScope, applyScopeConstraint, compileScope } from './scope.js';
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,gBAAgB,EAChB,UAAU,EACV,oBAAoB,EACpB,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,GACrB,MAAM,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAQhC,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EAEf,KAAK,WAAW,EAGjB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EAEf,KAAK,WAAW,EAGjB,MAAM,SAAS,CAAC;AAWjB,OAAO,KAAK,EAAE,iBAAiB,EAAc,MAAM,YAAY,CAAC;AAqFhE;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,WAAW,EACxB,IAAI,CAAC,EAAE,iBAAiB,GACvB,OAAO,CAAC,IAAI,CAAC,CAUf;AA4BD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,UAAU,EACtB,IAAI,CAAC,EAAE,iBAAiB,GACvB,OAAO,CAAC,IAAI,CAAC,CAkBf"}
|
package/dist/schema.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Table, TableColumn, } from 'typeorm';
|
|
2
2
|
import { TableUtils } from 'typeorm/schema-builder/util/TableUtils.js';
|
|
3
|
-
import { DEFAULT_TABLE_NAMES, PermissionEntity, RoleEntity, RolePermissionEntity, UserRoleEntity, } from './entities.js';
|
|
3
|
+
import { DEFAULT_TABLE_NAMES, PermissionEntity, RoleEntity, RolePermissionEntity, UserPermissionEntity, UserRoleEntity, } from './entities.js';
|
|
4
4
|
import { assertSafeIdentifier } from './sql.js';
|
|
5
5
|
/** Resolve the (schema-qualified) table name for an entity, applying BYO overrides. */
|
|
6
6
|
function tableName(base, schema) {
|
|
@@ -12,6 +12,7 @@ function resolveNames(tableNames) {
|
|
|
12
12
|
permissions: tableNames?.permissions ?? DEFAULT_TABLE_NAMES.permissions,
|
|
13
13
|
rolePermission: tableNames?.rolePermission ?? DEFAULT_TABLE_NAMES.rolePermission,
|
|
14
14
|
userRole: tableNames?.userRole ?? DEFAULT_TABLE_NAMES.userRole,
|
|
15
|
+
userPermission: tableNames?.userPermission ?? DEFAULT_TABLE_NAMES.userPermission,
|
|
15
16
|
};
|
|
16
17
|
}
|
|
17
18
|
/** The four table plans (entity + resolved name) for a given options set. */
|
|
@@ -29,16 +30,39 @@ function plans(opts) {
|
|
|
29
30
|
{ entity: PermissionEntity, name: tableName(names.permissions, schema) },
|
|
30
31
|
{ entity: RolePermissionEntity, name: tableName(names.rolePermission, schema) },
|
|
31
32
|
{ entity: UserRoleEntity, name: tableName(names.userRole, schema) },
|
|
33
|
+
{ entity: UserPermissionEntity, name: tableName(names.userPermission, schema) },
|
|
32
34
|
];
|
|
33
35
|
}
|
|
34
36
|
/**
|
|
35
37
|
* Build a driver-portable {@link Table} from entity metadata, overriding the table name
|
|
36
38
|
* so BYO names + an optional Postgres schema are honored (the metadata's own name is the
|
|
37
39
|
* entity-decorator default).
|
|
40
|
+
*
|
|
41
|
+
* Constraint-name rewrite (Postgres-critical): TypeORM derives index / unique / primary-key
|
|
42
|
+
* names by hashing the table name + columns (see `DefaultNamingStrategy.indexName` et al.).
|
|
43
|
+
* `Table.create` computes those from the entity's DEFAULT name, so simply reassigning
|
|
44
|
+
* `table.name` leaves the constraints carrying the OLD name's hash. Two tables created from
|
|
45
|
+
* the SAME entity metadata under DIFFERENT runtime names (BYO table names, or a per-test
|
|
46
|
+
* unique prefix) then emit IDENTICAL constraint names. SQLite scopes index names per-table
|
|
47
|
+
* and tolerates this; Postgres treats index/constraint names as schema-global and rejects
|
|
48
|
+
* the second `CREATE TABLE` with `relation "IDX_..." already exists`. So after renaming we
|
|
49
|
+
* regenerate every constraint name from the NEW table name via the connection's naming
|
|
50
|
+
* strategy, keeping each created table's constraints globally unique.
|
|
38
51
|
*/
|
|
39
|
-
function tableFromMetadata(metadata, driver, name) {
|
|
52
|
+
function tableFromMetadata(connection, metadata, driver, name) {
|
|
40
53
|
const table = Table.create(metadata, driver);
|
|
41
54
|
table.name = name;
|
|
55
|
+
const naming = connection.namingStrategy;
|
|
56
|
+
for (const index of table.indices) {
|
|
57
|
+
index.name = naming.indexName(name, index.columnNames, index.where);
|
|
58
|
+
}
|
|
59
|
+
for (const unique of table.uniques) {
|
|
60
|
+
unique.name = naming.uniqueConstraintName(name, unique.columnNames);
|
|
61
|
+
}
|
|
62
|
+
for (const check of table.checks) {
|
|
63
|
+
if (check.expression)
|
|
64
|
+
check.name = naming.checkConstraintName(name, check.expression);
|
|
65
|
+
}
|
|
42
66
|
return table;
|
|
43
67
|
}
|
|
44
68
|
/**
|
|
@@ -60,7 +84,7 @@ export async function createAuthzTables(queryRunner, opts) {
|
|
|
60
84
|
if (await queryRunner.hasTable(plan.name))
|
|
61
85
|
continue;
|
|
62
86
|
const metadata = queryRunner.connection.getMetadata(plan.entity);
|
|
63
|
-
await queryRunner.createTable(tableFromMetadata(metadata, driver, plan.name), true);
|
|
87
|
+
await queryRunner.createTable(tableFromMetadata(queryRunner.connection, metadata, driver, plan.name), true);
|
|
64
88
|
}
|
|
65
89
|
}
|
|
66
90
|
/**
|
|
@@ -105,7 +129,7 @@ export async function ensureAuthzSchema(dataSource, opts) {
|
|
|
105
129
|
}
|
|
106
130
|
else {
|
|
107
131
|
const metadata = queryRunner.connection.getMetadata(plan.entity);
|
|
108
|
-
await queryRunner.createTable(tableFromMetadata(metadata, driver, plan.name), true);
|
|
132
|
+
await queryRunner.createTable(tableFromMetadata(queryRunner.connection, metadata, driver, plan.name), true);
|
|
109
133
|
}
|
|
110
134
|
}
|
|
111
135
|
}
|
package/dist/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,EACL,WAAW,GACZ,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,UAAU,EAAE,MAAM,2CAA2C,CAAC;AACvE,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,UAAU,EACV,oBAAoB,EACpB,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAShD,uFAAuF;AACvF,SAAS,SAAS,CAAC,IAAY,EAAE,MAA0B;IACzD,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7C,CAAC;AAED,SAAS,YAAY,CAAC,UAAkC;
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/scope.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { ScopeConstraint } from '@dudousxd/nestjs-authz';
|
|
2
|
+
import type { Type } from '@nestjs/common';
|
|
3
|
+
import type { ObjectLiteral, SelectQueryBuilder } from 'typeorm';
|
|
4
|
+
/**
|
|
5
|
+
* The minimal scope-resolving surface shared by `Gate` and `BoundGate`. Accepting
|
|
6
|
+
* this (rather than `Gate`) lets {@link applyScope} take either a Gate (current/context
|
|
7
|
+
* user) or a `gate.forUser(...)` BoundGate without a cast.
|
|
8
|
+
*/
|
|
9
|
+
export interface ScopeResolver {
|
|
10
|
+
scope(entity: Type<unknown>, ability?: string): Promise<ScopeConstraint>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A compiled scope predicate: a SQL fragment using TypeORM named placeholders
|
|
14
|
+
* (`:name`, `:...name` for `IN`) plus the parameter map to bind. TypeORM binds the
|
|
15
|
+
* values itself (dialect-correct), so we never interpolate values — only identifiers,
|
|
16
|
+
* which are validated by {@link assertSafeIdentifier}.
|
|
17
|
+
*/
|
|
18
|
+
export interface CompiledScope {
|
|
19
|
+
sql: string;
|
|
20
|
+
params: ObjectLiteral;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Quote a SQL identifier. Defaults to ANSI double-quotes (Postgres / SQLite), but a
|
|
24
|
+
* dialect-correct escaper can be injected — MySQL/MariaDB quote with backticks and
|
|
25
|
+
* REJECT `"..."` as an identifier, so `applyScopeConstraint` passes the DataSource's
|
|
26
|
+
* `driver.escape` to make the compiled predicate portable across engines.
|
|
27
|
+
*/
|
|
28
|
+
export type EscapeIdentifier = (identifier: string) => string;
|
|
29
|
+
/**
|
|
30
|
+
* Compile an ORM-neutral {@link ScopeConstraint} into a parameterized SQL predicate
|
|
31
|
+
* for TypeORM. Returns:
|
|
32
|
+
* - `undefined` for `allow-all` — the caller adds NO `WHERE` (every row is visible);
|
|
33
|
+
* - `{ sql: '1 = 0', params: {} }` for `deny-all` — an always-false predicate;
|
|
34
|
+
* - otherwise the compiled condition tree.
|
|
35
|
+
*
|
|
36
|
+
* `paramPrefix` namespaces the generated placeholders so multiple `applyScope` calls
|
|
37
|
+
* (or other `andWhere`s) on one query never collide. `alias` (the entity's query
|
|
38
|
+
* alias) prefixes every column as `"alias"."field"`; omit it to emit bare `"field"`.
|
|
39
|
+
*
|
|
40
|
+
* SAFE: column names and the alias are interpolated into SQL, so each is validated
|
|
41
|
+
* against the package's `assertSafeIdentifier` allowlist (letters/digits/underscore,
|
|
42
|
+
* not leading-digit) — a hostile field/alias throws. Values are NEVER interpolated;
|
|
43
|
+
* they are returned in `params` for TypeORM to bind.
|
|
44
|
+
*
|
|
45
|
+
* `escapeId` controls identifier quoting; it defaults to ANSI double-quotes (Postgres /
|
|
46
|
+
* SQLite). `applyScopeConstraint` injects the DataSource's `driver.escape` so the
|
|
47
|
+
* predicate is also valid on MySQL/MariaDB (backtick-quoted), which rejects `"..."`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function compileScope(constraint: ScopeConstraint, paramPrefix: string, alias?: string, escapeId?: EscapeIdentifier): CompiledScope | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Apply a {@link ScopeConstraint} to a TypeORM {@link SelectQueryBuilder} as an
|
|
52
|
+
* `andWhere`, parameterized and identifier-safe. `allow-all` adds nothing; `deny-all`
|
|
53
|
+
* adds an always-false predicate so the query returns no rows.
|
|
54
|
+
*
|
|
55
|
+
* The query's primary alias is used to qualify columns (so the predicate is correct
|
|
56
|
+
* across joins). Each call uses a fresh parameter prefix, so repeated calls never
|
|
57
|
+
* clash. Returns the same builder for chaining.
|
|
58
|
+
*/
|
|
59
|
+
export declare function applyScopeConstraint<T extends ObjectLiteral>(qb: SelectQueryBuilder<T>, constraint: ScopeConstraint): SelectQueryBuilder<T>;
|
|
60
|
+
/**
|
|
61
|
+
* Ergonomic entry point: resolve the query scope for the current (context) user via
|
|
62
|
+
* the {@link Gate} and apply it to `qb` in one call. Equivalent to
|
|
63
|
+
* `applyScopeConstraint(qb, await gate.scope(entity, ability))`.
|
|
64
|
+
*
|
|
65
|
+
* The manual path (`gate.scope(...)` + `applyScopeConstraint(...)`) remains primary;
|
|
66
|
+
* this just folds the two steps together for the common list-endpoint case.
|
|
67
|
+
*
|
|
68
|
+
* ```ts
|
|
69
|
+
* const qb = repo.createQueryBuilder('post');
|
|
70
|
+
* await applyScope(qb, gate, Post); // current user's accessible posts
|
|
71
|
+
* const posts = await qb.getMany();
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export declare function applyScope<T extends ObjectLiteral>(qb: SelectQueryBuilder<T>, gate: ScopeResolver, entity: Type<unknown>, ability?: string): Promise<SelectQueryBuilder<T>>;
|
|
75
|
+
//# sourceMappingURL=scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,eAAe,EAGhB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAGjE;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;CAC1E;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,aAAa,CAAC;CACvB;AAKD;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;AAa9D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,CAC1B,UAAU,EAAE,eAAe,EAC3B,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,MAAM,EACd,QAAQ,GAAE,gBAA4B,GACrC,aAAa,GAAG,SAAS,CA+C3B;AAID;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,aAAa,EAC1D,EAAE,EAAE,kBAAkB,CAAC,CAAC,CAAC,EACzB,UAAU,EAAE,eAAe,GAC1B,kBAAkB,CAAC,CAAC,CAAC,CAQvB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,UAAU,CAAC,CAAC,SAAS,aAAa,EACtD,EAAE,EAAE,kBAAkB,CAAC,CAAC,CAAC,EACzB,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,EACrB,OAAO,SAAY,GAClB,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAEhC"}
|