@dudousxd/nestjs-authz 0.7.0 → 0.8.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 +14 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/store-kit.d.ts +38 -0
- package/dist/store-kit.d.ts.map +1 -1
- package/dist/store-kit.js +28 -0
- package/dist/store-kit.js.map +1 -1
- package/package.json +1 -1
- package/skills/authz-rbac-seams/SKILL.md +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @dudousxd/nestjs-authz
|
|
2
2
|
|
|
3
|
+
## 0.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#61](https://github.com/DavideCarvalho/nestjs-authz/pull/61) [`d1c719d`](https://github.com/DavideCarvalho/nestjs-authz/commit/d1c719d9ee973bc802ba8bf6854246875c4c2da9) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Admin management operations on every RBAC store adapter (TypeORM, MikroORM, Prisma, Drizzle), with the same semantics everywhere:
|
|
8
|
+
|
|
9
|
+
- `deleteRole(roleName): Promise<boolean>` deletes the role, its role→permission links and every user assignment of it (all tenants, all sources) in one transaction. Returns whether the role existed. Permissions are kept.
|
|
10
|
+
- `syncRolePermissions(roleName, permissionNames): Promise<void>` replaces the role's permission set with exactly these names (spatie's `syncPermissions`), creating the role and permissions by name when missing.
|
|
11
|
+
- `getRolePermissions(roleNames): Promise<Record<string, string[]>>` returns each role's sorted permission names without N+1 queries. Every existing requested role is a key, even with no permissions (`[]`). Roles that don't exist are absent.
|
|
12
|
+
- `listRoleAssignments(filter?): Promise<UserRoleAssignment[]>` returns raw `{ userType, userId, role, source, tenantId }` rows for admin listings, ordered by `(userType, userId, role, source, tenantId)`. Filters: `role` (a name or a list), `user`, `source`, and `tenantId`. Omitting `tenantId` applies no tenant filter, `null` returns global assignments only, and a string returns only that tenant's scoped assignments, without the global ones. MikroORM and Prisma have no tenant-scoped assignments, so there a tenant id string matches nothing.
|
|
13
|
+
- `removeUser(user): Promise<void>` deletes every role assignment of the user (all tenants, all sources) and, on TypeORM and Drizzle, every direct permission.
|
|
14
|
+
|
|
15
|
+
Core's `store-kit` exports the `RoleAssignmentFilter` and `UserRoleAssignment` types, plus the `roleFilterNames` and `compareUserRoleAssignments` helpers the adapters share. The adapters now require `@dudousxd/nestjs-authz >= 0.8.0`.
|
|
16
|
+
|
|
3
17
|
## 0.7.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Rewritten from this package's package.json by `scripts/sync-version.mjs`, which the root
|
|
2
2
|
// `version-packages` script runs right after `changeset version`. The build is plain `tsc`
|
|
3
3
|
// and injects nothing, so the literal below is what ships in dist.
|
|
4
|
-
export const VERSION = '0.
|
|
4
|
+
export const VERSION = '0.8.0';
|
|
5
5
|
export { Gate, BoundGate } from './gate.js';
|
|
6
6
|
export { PermissionCache } from './permission-cache.js';
|
|
7
7
|
export { authzDecisionChannel, publishAuthzDecision } from './diagnostics.js';
|
package/dist/store-kit.d.ts
CHANGED
|
@@ -38,4 +38,42 @@ export interface RoleAssignment {
|
|
|
38
38
|
/** Tenant the assignment is scoped to, or `null` for a global one (tenant-aware adapters). */
|
|
39
39
|
tenantId: string | null;
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* One persisted role assignment together with the user holding it — a raw row of the
|
|
43
|
+
* user↔role pivot, as returned by the adapters' `listRoleAssignments` (admin listings such as
|
|
44
|
+
* "every member's roles in tenant T" or "who holds role R").
|
|
45
|
+
*/
|
|
46
|
+
export interface UserRoleAssignment extends RoleAssignment {
|
|
47
|
+
/** The user's type (`'user'` unless the assignment was made with a typed {@link UserRef}). */
|
|
48
|
+
userType: string;
|
|
49
|
+
/** The user's id, stringified. */
|
|
50
|
+
userId: string;
|
|
51
|
+
}
|
|
52
|
+
/** Filter for the adapters' `listRoleAssignments`. Every field is optional; they AND together. */
|
|
53
|
+
export interface RoleAssignmentFilter {
|
|
54
|
+
/**
|
|
55
|
+
* Tenant filter:
|
|
56
|
+
* - `undefined` (omitted): no tenant filter — global AND every tenant's assignments;
|
|
57
|
+
* - `null`: GLOBAL assignments only;
|
|
58
|
+
* - a string: exactly that tenant's scoped assignments. Global assignments are NOT included
|
|
59
|
+
* (unlike the permission checks, where a global assignment applies in every tenant) — pass
|
|
60
|
+
* `null` in a second call when you need them too.
|
|
61
|
+
*
|
|
62
|
+
* Adapters without tenant support store every assignment as global, so a string matches nothing.
|
|
63
|
+
*/
|
|
64
|
+
tenantId?: string | null;
|
|
65
|
+
/** Only assignments of this role, or of any of these roles (an empty list matches nothing). */
|
|
66
|
+
role?: string | readonly string[];
|
|
67
|
+
/** Only this user's assignments. */
|
|
68
|
+
user?: UserRef;
|
|
69
|
+
/** Only assignments from this source (`'manual'`, `'sso'`, …). */
|
|
70
|
+
source?: string;
|
|
71
|
+
}
|
|
72
|
+
/** Normalize {@link RoleAssignmentFilter.role} to a list (`undefined` = no role filter). */
|
|
73
|
+
export declare function roleFilterNames(role: RoleAssignmentFilter['role']): string[] | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Deterministic order for `listRoleAssignments` rows: `(userType, userId, role, source, tenantId)`,
|
|
76
|
+
* by code unit (not locale), so every adapter returns the same order for the same data.
|
|
77
|
+
*/
|
|
78
|
+
export declare function compareUserRoleAssignments(a: UserRoleAssignment, b: UserRoleAssignment): number;
|
|
41
79
|
//# sourceMappingURL=store-kit.d.ts.map
|
package/dist/store-kit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-kit.d.ts","sourceRoot":"","sources":["../src/store-kit.ts"],"names":[],"mappings":"AAQA;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;CACrB;AAED,4EAA4E;AAC5E,MAAM,MAAM,OAAO,GAAG,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;AAErD;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,2EAA2E;AAC3E,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAK3E;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAE5C,wFAAwF;AACxF,MAAM,WAAW,cAAc;IAC7B,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,8FAA8F;IAC9F,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB"}
|
|
1
|
+
{"version":3,"file":"store-kit.d.ts","sourceRoot":"","sources":["../src/store-kit.ts"],"names":[],"mappings":"AAQA;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;CACrB;AAED,4EAA4E;AAC5E,MAAM,MAAM,OAAO,GAAG,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;AAErD;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,2EAA2E;AAC3E,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAK3E;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAE5C,wFAAwF;AACxF,MAAM,WAAW,cAAc;IAC7B,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,8FAA8F;IAC9F,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,8FAA8F;IAC9F,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,kGAAkG;AAClG,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,+FAA+F;IAC/F,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IAClC,oCAAoC;IACpC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,4FAA4F;AAC5F,wBAAgB,eAAe,CAAC,IAAI,EAAE,oBAAoB,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,GAAG,SAAS,CAGxF;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,EAAE,kBAAkB,EAAE,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAgB/F"}
|
package/dist/store-kit.js
CHANGED
|
@@ -17,4 +17,32 @@ export function normalizeUserRef(ref) {
|
|
|
17
17
|
* (`assignRole`) and every row that predates per-source assignments.
|
|
18
18
|
*/
|
|
19
19
|
export const DEFAULT_ROLE_SOURCE = 'manual';
|
|
20
|
+
/** Normalize {@link RoleAssignmentFilter.role} to a list (`undefined` = no role filter). */
|
|
21
|
+
export function roleFilterNames(role) {
|
|
22
|
+
if (role === undefined)
|
|
23
|
+
return undefined;
|
|
24
|
+
return typeof role === 'string' ? [role] : [...new Set(role)];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Deterministic order for `listRoleAssignments` rows: `(userType, userId, role, source, tenantId)`,
|
|
28
|
+
* by code unit (not locale), so every adapter returns the same order for the same data.
|
|
29
|
+
*/
|
|
30
|
+
export function compareUserRoleAssignments(a, b) {
|
|
31
|
+
const keys = (x) => [
|
|
32
|
+
x.userType,
|
|
33
|
+
x.userId,
|
|
34
|
+
x.role,
|
|
35
|
+
x.source,
|
|
36
|
+
x.tenantId ?? '',
|
|
37
|
+
];
|
|
38
|
+
const ka = keys(a);
|
|
39
|
+
const kb = keys(b);
|
|
40
|
+
for (let i = 0; i < ka.length; i++) {
|
|
41
|
+
const l = ka[i];
|
|
42
|
+
const r = kb[i];
|
|
43
|
+
if (l !== r)
|
|
44
|
+
return l < r ? -1 : 1;
|
|
45
|
+
}
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
20
48
|
//# sourceMappingURL=store-kit.js.map
|
package/dist/store-kit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-kit.js","sourceRoot":"","sources":["../src/store-kit.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,EAAE;AACF,8EAA8E;AAC9E,kFAAkF;AAClF,8EAA8E;AAC9E,kFAAkF;AAClF,+EAA+E;AAuB/E,2EAA2E;AAC3E,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACvD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3C,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAC"}
|
|
1
|
+
{"version":3,"file":"store-kit.js","sourceRoot":"","sources":["../src/store-kit.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,EAAE;AACF,8EAA8E;AAC9E,kFAAkF;AAClF,8EAA8E;AAC9E,kFAAkF;AAClF,+EAA+E;AAuB/E,2EAA2E;AAC3E,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACvD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3C,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;AAC1D,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAC;AAgD5C,4FAA4F;AAC5F,MAAM,UAAU,eAAe,CAAC,IAAkC;IAChE,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACzC,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;AAChE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CAAC,CAAqB,EAAE,CAAqB;IACrF,MAAM,IAAI,GAAG,CAAC,CAAqB,EAAE,EAAE,CAAC;QACtC,CAAC,CAAC,QAAQ;QACV,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,QAAQ,IAAI,EAAE;KACjB,CAAC;IACF,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACnB,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAW,CAAC;QAC1B,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAW,CAAC;QAC1B,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
|
package/package.json
CHANGED
|
@@ -62,6 +62,13 @@ await gate.forUser(user).allows('posts.publish'); // true via PERMISSION_PROVIDE
|
|
|
62
62
|
await gate.forUser(user).hasRole('editor'); // true via ROLE_PROVIDER
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
+
Admin-UI management lives on every adapter's store too (same semantics everywhere):
|
|
66
|
+
`syncRolePermissions(role, names)` replaces a role's permission set, `getRolePermissions(roles)`
|
|
67
|
+
returns `{ [role]: permissionNames }` (missing roles absent), `deleteRole(role)` removes the role
|
|
68
|
+
with its links and assignments, `listRoleAssignments({ tenantId?, role?, user?, source? })` lists
|
|
69
|
+
raw `{ userType, userId, role, source, tenantId }` rows (`tenantId: null` = global only, a string =
|
|
70
|
+
that tenant's scoped rows only), and `removeUser(user)` drops all of a user's assignments.
|
|
71
|
+
|
|
65
72
|
## Core patterns
|
|
66
73
|
|
|
67
74
|
### A hand-rolled PermissionProvider
|