@generazioneai/authz 0.0.2 → 0.0.4

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.
Files changed (61) hide show
  1. package/dist/enforce/index.d.ts +4 -0
  2. package/dist/enforce/index.d.ts.map +1 -0
  3. package/dist/enforce/index.js +21 -0
  4. package/dist/enforce/index.js.map +1 -0
  5. package/dist/enforce/prisma-extension.d.ts +25 -0
  6. package/dist/enforce/prisma-extension.d.ts.map +1 -0
  7. package/dist/enforce/prisma-extension.js +79 -0
  8. package/dist/enforce/prisma-extension.js.map +1 -0
  9. package/dist/enforce/run-unscoped.d.ts +2 -0
  10. package/dist/enforce/run-unscoped.d.ts.map +1 -0
  11. package/dist/enforce/run-unscoped.js +14 -0
  12. package/dist/enforce/run-unscoped.js.map +1 -0
  13. package/dist/enforce/scoped-repository.d.ts +10 -0
  14. package/dist/enforce/scoped-repository.d.ts.map +1 -0
  15. package/dist/enforce/scoped-repository.js +26 -0
  16. package/dist/enforce/scoped-repository.js.map +1 -0
  17. package/dist/nest/authorize.decorator.d.ts +3 -0
  18. package/dist/nest/authorize.decorator.d.ts.map +1 -0
  19. package/dist/nest/authorize.decorator.js +15 -0
  20. package/dist/nest/authorize.decorator.js.map +1 -0
  21. package/dist/nest/global-authz.guard.d.ts +34 -0
  22. package/dist/nest/global-authz.guard.d.ts.map +1 -0
  23. package/dist/nest/global-authz.guard.js +82 -0
  24. package/dist/nest/global-authz.guard.js.map +1 -0
  25. package/dist/nest/index.d.ts +3 -0
  26. package/dist/nest/index.d.ts.map +1 -1
  27. package/dist/nest/index.js +3 -0
  28. package/dist/nest/index.js.map +1 -1
  29. package/dist/nest/public.decorator.d.ts +3 -0
  30. package/dist/nest/public.decorator.d.ts.map +1 -0
  31. package/dist/nest/public.decorator.js +10 -0
  32. package/dist/nest/public.decorator.js.map +1 -0
  33. package/dist/snapshot/ability-builder.d.ts +22 -0
  34. package/dist/snapshot/ability-builder.d.ts.map +1 -0
  35. package/dist/snapshot/ability-builder.js +40 -0
  36. package/dist/snapshot/ability-builder.js.map +1 -0
  37. package/dist/snapshot/distributed-lock.d.ts +19 -0
  38. package/dist/snapshot/distributed-lock.d.ts.map +1 -0
  39. package/dist/snapshot/distributed-lock.js +37 -0
  40. package/dist/snapshot/distributed-lock.js.map +1 -0
  41. package/dist/snapshot/index.d.ts +7 -0
  42. package/dist/snapshot/index.d.ts.map +1 -0
  43. package/dist/snapshot/index.js +26 -0
  44. package/dist/snapshot/index.js.map +1 -0
  45. package/dist/snapshot/l1-cache.d.ts +16 -0
  46. package/dist/snapshot/l1-cache.d.ts.map +1 -0
  47. package/dist/snapshot/l1-cache.js +43 -0
  48. package/dist/snapshot/l1-cache.js.map +1 -0
  49. package/dist/snapshot/perm-hash.d.ts +3 -0
  50. package/dist/snapshot/perm-hash.d.ts.map +1 -0
  51. package/dist/snapshot/perm-hash.js +33 -0
  52. package/dist/snapshot/perm-hash.js.map +1 -0
  53. package/dist/snapshot/snapshot.envelope.d.ts +36 -0
  54. package/dist/snapshot/snapshot.envelope.d.ts.map +1 -0
  55. package/dist/snapshot/snapshot.envelope.js +23 -0
  56. package/dist/snapshot/snapshot.envelope.js.map +1 -0
  57. package/dist/snapshot/snapshot.store.d.ts +24 -0
  58. package/dist/snapshot/snapshot.store.d.ts.map +1 -0
  59. package/dist/snapshot/snapshot.store.js +78 -0
  60. package/dist/snapshot/snapshot.store.js.map +1 -0
  61. package/package.json +26 -5
@@ -0,0 +1,4 @@
1
+ export * from './prisma-extension';
2
+ export * from './run-unscoped';
3
+ export * from './scoped-repository';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/enforce/index.ts"],"names":[],"mappings":"AACA,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ // @generazioneai/authz/enforce — Step 3 query-level enforcement (no NestJS runtime dep).
18
+ __exportStar(require("./prisma-extension"), exports);
19
+ __exportStar(require("./run-unscoped"), exports);
20
+ __exportStar(require("./scoped-repository"), exports);
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/enforce/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,yFAAyF;AACzF,qDAAmC;AACnC,iDAA+B;AAC/B,sDAAoC"}
@@ -0,0 +1,25 @@
1
+ import type { ResourceRegistry } from '../resource-registry';
2
+ export type EnforceMode = 'off' | 'shadow' | 'enforce';
3
+ export interface AuthzExtensionOptions {
4
+ mode?: EnforceMode;
5
+ /** called in shadow mode with what WOULD be restricted (metrics/logging). */
6
+ onShadow?: (info: {
7
+ model: string;
8
+ operation: string;
9
+ subject: string;
10
+ }) => void;
11
+ }
12
+ export declare function createAuthzPrismaExtension(registry: ResourceRegistry, opts?: AuthzExtensionOptions): {
13
+ name: string;
14
+ query: {
15
+ $allModels: {
16
+ $allOperations({ model, operation, args, query }: {
17
+ model: string;
18
+ operation: string;
19
+ args: any;
20
+ query: (a: any) => Promise<unknown>;
21
+ }): Promise<unknown>;
22
+ };
23
+ };
24
+ };
25
+ //# sourceMappingURL=prisma-extension.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prisma-extension.d.ts","sourceRoot":"","sources":["../../src/enforce/prisma-extension.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAC;AAiBvD,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAClF;AAED,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,gBAAgB,EAAE,IAAI,GAAE,qBAA0B;;;;8DAOvC;gBAAE,KAAK,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAC;gBAAC,IAAI,EAAE,GAAG,CAAC;gBAAC,KAAK,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;aAAE;;;EAyCnJ"}
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAuthzPrismaExtension = createAuthzPrismaExtension;
4
+ // Step 3 — Prisma Client extension that enforces the request's CASL ability on EVERY
5
+ // query (the safety net: impossible to bypass from inside the service). Reads the ability
6
+ // from ALS, looks up the model's Subject in the registry, and ANDs accessibleBy(ability,
7
+ // action)[Subject] into the where (reads/updates/deletes) or stamps tenancy on creates.
8
+ //
9
+ // Mode (AUTHZ_ENFORCE_MODE, default 'off'):
10
+ // off → passthrough (no behaviour change — safe to ship).
11
+ // shadow → compute but DON'T apply; log would-restrict (readiness measurement).
12
+ // enforce → apply (fail-closed: no ALS context / no ability → throw).
13
+ const prisma_1 = require("@casl/prisma");
14
+ const als_1 = require("../context/als");
15
+ const READ = new Set(['findUnique', 'findUniqueOrThrow', 'findFirst', 'findFirstOrThrow', 'findMany', 'count', 'aggregate', 'groupBy']);
16
+ const UPDATE = new Set(['update', 'updateMany', 'upsert']);
17
+ const DELETE = new Set(['delete', 'deleteMany']);
18
+ const CREATE = new Set(['create', 'createMany', 'createManyAndReturn']);
19
+ function opToAction(op) {
20
+ if (READ.has(op))
21
+ return 'read';
22
+ if (CREATE.has(op))
23
+ return 'create';
24
+ if (UPDATE.has(op))
25
+ return 'update';
26
+ if (DELETE.has(op))
27
+ return 'delete';
28
+ return 'manage';
29
+ }
30
+ const lowerFirst = (s) => (s ? s.charAt(0).toLowerCase() + s.slice(1) : s);
31
+ function createAuthzPrismaExtension(registry, opts = {}) {
32
+ const mode = opts.mode ?? process.env.AUTHZ_ENFORCE_MODE ?? 'off';
33
+ return {
34
+ name: 'skillera-authz-enforce',
35
+ query: {
36
+ $allModels: {
37
+ async $allOperations({ model, operation, args, query }) {
38
+ if (mode === 'off')
39
+ return query(args);
40
+ const ctx = als_1.authzAls.getStore();
41
+ if (!ctx) {
42
+ if (mode === 'enforce')
43
+ throw new Error(`[authz] UNSCOPED_QUERY ${model}.${operation} — no request context`);
44
+ return query(args);
45
+ }
46
+ if (ctx.unscoped)
47
+ return query(args); // runUnscoped escape hatch (system tasks)
48
+ if (!ctx.ability) {
49
+ if (mode === 'enforce')
50
+ throw new Error(`[authz] no ability in context for ${model}.${operation}`);
51
+ return query(args);
52
+ }
53
+ const manifest = registry.get(model) ?? registry.byPrismaModel(lowerFirst(model));
54
+ if (!manifest)
55
+ return query(args); // unmanaged model → not scoped
56
+ const subject = manifest.subject;
57
+ const action = opToAction(operation);
58
+ if (mode === 'shadow') {
59
+ opts.onShadow?.({ model, operation, subject });
60
+ return query(args);
61
+ }
62
+ // enforce
63
+ if (CREATE.has(operation)) {
64
+ const t = manifest.tenancy;
65
+ if (t && t.kind === 'single' && ctx.tenantId && args?.data) {
66
+ const stamp = (d) => ({ [t.field]: ctx.tenantId, ...d });
67
+ args = { ...args, data: Array.isArray(args.data) ? args.data.map(stamp) : stamp(args.data) };
68
+ }
69
+ return query(args);
70
+ }
71
+ const scopeWhere = (0, prisma_1.accessibleBy)(ctx.ability, action)[subject];
72
+ args = { ...args, where: args?.where ? { AND: [args.where, scopeWhere] } : scopeWhere };
73
+ return query(args);
74
+ },
75
+ },
76
+ },
77
+ };
78
+ }
79
+ //# sourceMappingURL=prisma-extension.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prisma-extension.js","sourceRoot":"","sources":["../../src/enforce/prisma-extension.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function runUnscoped<T>(reason: string, fn: () => T): T;
2
+ //# sourceMappingURL=run-unscoped.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-unscoped.d.ts","sourceRoot":"","sources":["../../src/enforce/run-unscoped.ts"],"names":[],"mappings":"AAMA,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAI7D"}
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runUnscoped = runUnscoped;
4
+ // Step 3 DEC-S3.17 — escape hatch for legitimate system work that must bypass scoping
5
+ // (seeds, cross-tenant admin jobs, the snapshot builder itself). Sets ctx.unscoped so the
6
+ // Prisma extension lets queries through. Requires an explicit reason (lint-enforced, Step 8).
7
+ const als_1 = require("../context/als");
8
+ const authz_context_1 = require("../context/authz-context");
9
+ function runUnscoped(reason, fn) {
10
+ const current = als_1.authzAls.getStore();
11
+ const ctx = { ...(current ?? authz_context_1.EMPTY_CTX), unscoped: true, unscopedReason: reason };
12
+ return als_1.authzAls.run(ctx, fn);
13
+ }
14
+ //# sourceMappingURL=run-unscoped.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-unscoped.js","sourceRoot":"","sources":["../../src/enforce/run-unscoped.ts"],"names":[],"mappings":";;AAMA,kCAIC;AAVD,sFAAsF;AACtF,0FAA0F;AAC1F,8FAA8F;AAC9F,wCAA0C;AAC1C,4DAAwE;AAExE,SAAgB,WAAW,CAAI,MAAc,EAAE,EAAW;IACxD,MAAM,OAAO,GAAG,cAAQ,CAAC,QAAQ,EAAE,CAAC;IACpC,MAAM,GAAG,GAAiB,EAAE,GAAG,CAAC,OAAO,IAAI,yBAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC;IAChG,OAAO,cAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { AuthzContext } from '../context/authz-context';
2
+ export declare class ScopedRepository<TDelegate> {
3
+ private readonly delegate;
4
+ constructor(delegate: TDelegate);
5
+ /** Current request context (throws if missing — enforcement is never accidental). */
6
+ protected ctx(): AuthzContext;
7
+ /** The underlying (extension-scoped) Prisma delegate. */
8
+ get scoped(): TDelegate;
9
+ }
10
+ //# sourceMappingURL=scoped-repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scoped-repository.d.ts","sourceRoot":"","sources":["../../src/enforce/scoped-repository.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,qBAAa,gBAAgB,CAAC,SAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,SAAS;IAEhD,qFAAqF;IACrF,SAAS,CAAC,GAAG,IAAI,YAAY;IAM7B,yDAAyD;IACzD,IAAI,MAAM,IAAI,SAAS,CAGtB;CACF"}
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ScopedRepository = void 0;
4
+ // Step 3 DX wrapper — services inject a ScopedRepository instead of the raw Prisma
5
+ // delegate. The Prisma extension already enforces scope on every query; this surface
6
+ // makes the intent explicit and asserts a request context is present.
7
+ const als_1 = require("../context/als");
8
+ class ScopedRepository {
9
+ constructor(delegate) {
10
+ this.delegate = delegate;
11
+ }
12
+ /** Current request context (throws if missing — enforcement is never accidental). */
13
+ ctx() {
14
+ const c = als_1.authzAls.getStore();
15
+ if (!c)
16
+ throw new Error('[authz] ScopedRepository used outside a request context');
17
+ return c;
18
+ }
19
+ /** The underlying (extension-scoped) Prisma delegate. */
20
+ get scoped() {
21
+ this.ctx();
22
+ return this.delegate;
23
+ }
24
+ }
25
+ exports.ScopedRepository = ScopedRepository;
26
+ //# sourceMappingURL=scoped-repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scoped-repository.js","sourceRoot":"","sources":["../../src/enforce/scoped-repository.ts"],"names":[],"mappings":";;;AAAA,mFAAmF;AACnF,qFAAqF;AACrF,sEAAsE;AACtE,wCAA0C;AAG1C,MAAa,gBAAgB;IAC3B,YAA6B,QAAmB;QAAnB,aAAQ,GAAR,QAAQ,CAAW;IAAG,CAAC;IAEpD,qFAAqF;IAC3E,GAAG;QACX,MAAM,CAAC,GAAG,cAAQ,CAAC,QAAQ,EAAE,CAAC;QAC9B,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;QACnF,OAAO,CAAC,CAAC;IACX,CAAC;IAED,yDAAyD;IACzD,IAAI,MAAM;QACR,IAAI,CAAC,GAAG,EAAE,CAAC;QACX,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;CACF;AAfD,4CAeC"}
@@ -0,0 +1,3 @@
1
+ import { type CustomDecorator } from '@nestjs/common';
2
+ export declare function Authorize(action: string, subject: string): CustomDecorator;
3
+ //# sourceMappingURL=authorize.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authorize.decorator.d.ts","sourceRoot":"","sources":["../../src/nest/authorize.decorator.ts"],"names":[],"mappings":"AAMA,OAAO,EAAe,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGnE,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,eAAe,CAE1E"}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Authorize = Authorize;
4
+ // Step 6 — declare the permission a route requires. Read by GlobalAuthzGuard.
5
+ //
6
+ // @Authorize('read', 'JuridicalIndividual')
7
+ // @Get() findAll() { ... }
8
+ //
9
+ // Mode-gated by the guard (AUTHZ_ENFORCE_MODE): in 'off' this metadata is inert.
10
+ const common_1 = require("@nestjs/common");
11
+ const global_authz_guard_1 = require("./global-authz.guard");
12
+ function Authorize(action, subject) {
13
+ return (0, common_1.SetMetadata)(global_authz_guard_1.AUTHZ_REQUIREMENT_KEY, { action, subject });
14
+ }
15
+ //# sourceMappingURL=authorize.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authorize.decorator.js","sourceRoot":"","sources":["../../src/nest/authorize.decorator.ts"],"names":[],"mappings":";;AASA,8BAEC;AAXD,8EAA8E;AAC9E,EAAE;AACF,8CAA8C;AAC9C,6BAA6B;AAC7B,EAAE;AACF,iFAAiF;AACjF,2CAAmE;AACnE,6DAAoF;AAEpF,SAAgB,SAAS,CAAC,MAAc,EAAE,OAAe;IACvD,OAAO,IAAA,oBAAW,EAAC,0CAAqB,EAAE,EAAE,MAAM,EAAE,OAAO,EAA6B,CAAC,CAAC;AAC5F,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { type CanActivate, type ExecutionContext } from '@nestjs/common';
2
+ import type { Reflector } from '@nestjs/core';
3
+ import type { AuthzContext } from '../context/authz-context';
4
+ export type EnforceMode = 'off' | 'shadow' | 'enforce';
5
+ export declare const AUTHZ_REQUIREMENT_KEY = "skillera:authz:requirement";
6
+ export declare const PUBLIC_KEY = "skillera:authz:public";
7
+ export interface AuthzRequirement {
8
+ action: string;
9
+ subject: string;
10
+ }
11
+ /** Resolves the ability + context for an HTTP request (gateway-specific impl supplies it). */
12
+ export interface AbilityResolver {
13
+ resolve(req: unknown): Promise<AuthzContext | null>;
14
+ }
15
+ export interface GlobalAuthzGuardOptions {
16
+ mode?: EnforceMode;
17
+ /**
18
+ * Fallback requirement source, consulted when a route has no @Authorize.
19
+ * Lets a host reuse pre-existing route metadata (e.g. the gateway's legacy
20
+ * @CheckPolicies) instead of re-decorating every endpoint. Returns null when
21
+ * the route genuinely has no requirement (→ default-deny still applies).
22
+ */
23
+ requirementResolver?: (context: ExecutionContext, reflector: Reflector) => AuthzRequirement | null;
24
+ }
25
+ export declare class GlobalAuthzGuard implements CanActivate {
26
+ private readonly reflector;
27
+ private readonly resolver;
28
+ private readonly logger;
29
+ private readonly mode;
30
+ private readonly requirementResolver?;
31
+ constructor(reflector: Reflector, resolver: AbilityResolver, opts?: GlobalAuthzGuardOptions | EnforceMode);
32
+ canActivate(context: ExecutionContext): Promise<boolean>;
33
+ }
34
+ //# sourceMappingURL=global-authz.guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global-authz.guard.d.ts","sourceRoot":"","sources":["../../src/nest/global-authz.guard.ts"],"names":[],"mappings":"AAYA,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAC;AACvD,eAAO,MAAM,qBAAqB,+BAA+B,CAAC;AAClE,eAAO,MAAM,UAAU,0BAA0B,CAAC;AAElD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,8FAA8F;AAC9F,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,SAAS,KAAK,gBAAgB,GAAG,IAAI,CAAC;CACpG;AAED,qBACa,gBAAiB,YAAW,WAAW;IAMhD,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAN3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6B;IACpD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAiD;gBAGnE,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,eAAe,EAC1C,IAAI,GAAE,uBAAuB,GAAG,WAAgB;IAQ5C,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;CAwC/D"}
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.GlobalAuthzGuard = exports.PUBLIC_KEY = exports.AUTHZ_REQUIREMENT_KEY = void 0;
13
+ // Step 6 — gateway route-level enforcement (default-deny).
14
+ //
15
+ // Resolves the request's CASL ability (via a pluggable AbilityResolver — at the gateway,
16
+ // that fetches the user's permission snapshot from Redis/skillID), then checks the route's
17
+ // @Authorize(action, subject) requirement. Runs the rest of the request inside authzAls.run
18
+ // so the signing client / downstream propagation see the context.
19
+ //
20
+ // Mode (AUTHZ_ENFORCE_MODE, default 'off'):
21
+ // off → allow (no resolution, zero behaviour change).
22
+ // shadow → resolve + log would-deny, but ALLOW.
23
+ // enforce → default-deny: a route with no @Authorize and no @Public → 403; otherwise
24
+ // allow iff ability.can(action, subject).
25
+ const common_1 = require("@nestjs/common");
26
+ const als_1 = require("../context/als");
27
+ exports.AUTHZ_REQUIREMENT_KEY = 'skillera:authz:requirement';
28
+ exports.PUBLIC_KEY = 'skillera:authz:public';
29
+ let GlobalAuthzGuard = class GlobalAuthzGuard {
30
+ constructor(reflector, resolver, opts = {}) {
31
+ this.reflector = reflector;
32
+ this.resolver = resolver;
33
+ this.logger = new common_1.Logger('GlobalAuthz');
34
+ // Back-compat: a bare mode string is still accepted.
35
+ const o = typeof opts === 'string' ? { mode: opts } : opts;
36
+ this.mode = o.mode ?? process.env.AUTHZ_ENFORCE_MODE ?? 'off';
37
+ this.requirementResolver = o.requirementResolver;
38
+ }
39
+ async canActivate(context) {
40
+ if (this.mode === 'off' || context.getType() !== 'http')
41
+ return true;
42
+ const isPublic = this.reflector.getAllAndOverride(exports.PUBLIC_KEY, [context.getHandler(), context.getClass()]);
43
+ if (isPublic)
44
+ return true;
45
+ const requirement = this.reflector.getAllAndOverride(exports.AUTHZ_REQUIREMENT_KEY, [
46
+ context.getHandler(),
47
+ context.getClass(),
48
+ ]) ?? this.requirementResolver?.(context, this.reflector) ?? undefined;
49
+ const req = context.switchToHttp().getRequest();
50
+ const ctx = await this.resolver.resolve(req);
51
+ // Make the ability/context available downstream (signing client, etc.) for this request.
52
+ if (ctx) {
53
+ const als = als_1.authzAls.getStore();
54
+ if (als)
55
+ Object.assign(als, ctx);
56
+ else
57
+ req._authzCtx = ctx;
58
+ }
59
+ // Default-deny: a protected route MUST declare @Authorize (or @Public).
60
+ if (!requirement) {
61
+ if (this.mode === 'shadow') {
62
+ this.logger.warn(`shadow would-deny: ${req?.method} ${req?.url} has no @Authorize`);
63
+ return true;
64
+ }
65
+ throw new common_1.ForbiddenException('authz: endpoint has no permission requirement (@Authorize) declared');
66
+ }
67
+ const allowed = !!ctx?.ability?.can(requirement.action, requirement.subject);
68
+ if (allowed)
69
+ return true;
70
+ if (this.mode === 'shadow') {
71
+ this.logger.warn(`shadow would-deny ${requirement.action}:${requirement.subject} for user=${ctx?.userId ?? '?'} (${req?.method} ${req?.url})`);
72
+ return true;
73
+ }
74
+ throw new common_1.ForbiddenException(`authz: missing permission ${requirement.action}:${requirement.subject}`);
75
+ }
76
+ };
77
+ exports.GlobalAuthzGuard = GlobalAuthzGuard;
78
+ exports.GlobalAuthzGuard = GlobalAuthzGuard = __decorate([
79
+ (0, common_1.Injectable)(),
80
+ __metadata("design:paramtypes", [Function, Object, Object])
81
+ ], GlobalAuthzGuard);
82
+ //# sourceMappingURL=global-authz.guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global-authz.guard.js","sourceRoot":"","sources":["../../src/nest/global-authz.guard.ts"],"names":[],"mappings":"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"}
@@ -3,4 +3,7 @@ export * from './nats-scoped-client.proxy';
3
3
  export * from './internal-auth.interceptor';
4
4
  export * from './authz-context.middleware';
5
5
  export * from './authz-context.interceptor';
6
+ export * from './global-authz.guard';
7
+ export * from './public.decorator';
8
+ export * from './authorize.decorator';
6
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nest/index.ts"],"names":[],"mappings":"AAGA,cAAc,gCAAgC,CAAC;AAC/C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/nest/index.ts"],"names":[],"mappings":"AAGA,cAAc,gCAAgC,CAAC;AAC/C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC"}
@@ -22,4 +22,7 @@ __exportStar(require("./nats-scoped-client.proxy"), exports);
22
22
  __exportStar(require("./internal-auth.interceptor"), exports);
23
23
  __exportStar(require("./authz-context.middleware"), exports);
24
24
  __exportStar(require("./authz-context.interceptor"), exports);
25
+ __exportStar(require("./global-authz.guard"), exports);
26
+ __exportStar(require("./public.decorator"), exports);
27
+ __exportStar(require("./authorize.decorator"), exports);
25
28
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/nest/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,yFAAyF;AACzF,mFAAmF;AACnF,iCAAiC;AACjC,iEAA+C;AAC/C,6DAA2C;AAC3C,8DAA4C;AAC5C,6DAA2C;AAC3C,8DAA4C"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/nest/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,yFAAyF;AACzF,mFAAmF;AACnF,iCAAiC;AACjC,iEAA+C;AAC/C,6DAA2C;AAC3C,8DAA4C;AAC5C,6DAA2C;AAC3C,8DAA4C;AAC5C,uDAAqC;AACrC,qDAAmC;AACnC,wDAAsC"}
@@ -0,0 +1,3 @@
1
+ import { type CustomDecorator } from '@nestjs/common';
2
+ export declare function Public(): CustomDecorator;
3
+ //# sourceMappingURL=public.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public.decorator.d.ts","sourceRoot":"","sources":["../../src/nest/public.decorator.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGnE,wBAAgB,MAAM,IAAI,eAAe,CAExC"}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Public = Public;
4
+ // Step 6 — opt a route out of default-deny (login, health, public pages).
5
+ const common_1 = require("@nestjs/common");
6
+ const global_authz_guard_1 = require("./global-authz.guard");
7
+ function Public() {
8
+ return (0, common_1.SetMetadata)(global_authz_guard_1.PUBLIC_KEY, true);
9
+ }
10
+ //# sourceMappingURL=public.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public.decorator.js","sourceRoot":"","sources":["../../src/nest/public.decorator.ts"],"names":[],"mappings":";;AAIA,wBAEC;AAND,0EAA0E;AAC1E,2CAAmE;AACnE,6DAAkD;AAElD,SAAgB,MAAM;IACpB,OAAO,IAAA,oBAAW,EAAC,+BAAU,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { AuthzContext } from '../context/authz-context';
2
+ import type { ResourceRegistry } from '../resource-registry';
3
+ import type { AbilityRule } from './snapshot.envelope';
4
+ export type Scope = 'GLOBAL' | 'TENANT' | 'OWN' | 'CONNECTED' | 'PROVIDER' | 'CUSTOMER';
5
+ export interface Grant {
6
+ action: string;
7
+ /** subject literal, or 'all' for the CASL wildcard (system tier). */
8
+ subject: string;
9
+ scope: Scope;
10
+ /** field-level allow-list; empty/absent = all fields. */
11
+ fields?: string[];
12
+ inverted?: boolean;
13
+ }
14
+ /**
15
+ * Grants → serialized CASL rules. GLOBAL scope (and the 'all' wildcard) emit no
16
+ * conditions (unrestricted within the grant); every other scope pulls the manifest's
17
+ * `scopes[scope]` template and substitutes the $ctx placeholders against `ctx`.
18
+ */
19
+ export declare function buildRulesFromGrants(grants: Grant[], registry: ResourceRegistry, ctx: AuthzContext): AbilityRule[];
20
+ /** Rehydrate a PrismaAbility from serialized rules (snapshot → runtime). */
21
+ export declare function hydrateAbility(rules: AbilityRule[]): import("@casl/ability").PureAbility<any, any>;
22
+ //# sourceMappingURL=ability-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ability-builder.d.ts","sourceRoot":"","sources":["../../src/snapshot/ability-builder.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,MAAM,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,CAAC;AAExF,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC;IACb,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,KAAK,EAAE,EACf,QAAQ,EAAE,gBAAgB,EAC1B,GAAG,EAAE,YAAY,GAChB,WAAW,EAAE,CAiBf;AAED,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,iDAElD"}
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildRulesFromGrants = buildRulesFromGrants;
4
+ exports.hydrateAbility = hydrateAbility;
5
+ // Step 4 builder core — turn a user's role-permission GRANTS into CASL rules, then a
6
+ // PrismaAbility. A grant = (action × subject × scope [× fields]); the scope is resolved
7
+ // to a Prisma where via the resource manifest's scope template + substituteContext.
8
+ // (skillID flattens its 3-tier RBAC into grants and calls this.)
9
+ const prisma_1 = require("@casl/prisma");
10
+ const scope_substitute_1 = require("../scope-substitute");
11
+ /**
12
+ * Grants → serialized CASL rules. GLOBAL scope (and the 'all' wildcard) emit no
13
+ * conditions (unrestricted within the grant); every other scope pulls the manifest's
14
+ * `scopes[scope]` template and substitutes the $ctx placeholders against `ctx`.
15
+ */
16
+ function buildRulesFromGrants(grants, registry, ctx) {
17
+ const rules = [];
18
+ for (const g of grants) {
19
+ const rule = { action: g.action, subject: g.subject };
20
+ if (g.fields && g.fields.length)
21
+ rule.fields = g.fields;
22
+ if (g.inverted)
23
+ rule.inverted = true;
24
+ if (g.scope === 'GLOBAL' || g.subject === 'all') {
25
+ rules.push(rule);
26
+ continue;
27
+ }
28
+ const template = registry.get(g.subject)?.scopes?.[g.scope];
29
+ if (!template)
30
+ continue; // scope not declared for this subject (authz:check guards this)
31
+ rule.conditions = (0, scope_substitute_1.substituteContext)(template, ctx);
32
+ rules.push(rule);
33
+ }
34
+ return rules;
35
+ }
36
+ /** Rehydrate a PrismaAbility from serialized rules (snapshot → runtime). */
37
+ function hydrateAbility(rules) {
38
+ return (0, prisma_1.createPrismaAbility)(rules);
39
+ }
40
+ //# sourceMappingURL=ability-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ability-builder.js","sourceRoot":"","sources":["../../src/snapshot/ability-builder.ts"],"names":[],"mappings":";;AA2BA,oDAqBC;AAGD,wCAEC;AArDD,qFAAqF;AACrF,wFAAwF;AACxF,oFAAoF;AACpF,iEAAiE;AACjE,yCAAmD;AACnD,0DAAwD;AAiBxD;;;;GAIG;AACH,SAAgB,oBAAoB,CAClC,MAAe,EACf,QAA0B,EAC1B,GAAiB;IAEjB,MAAM,KAAK,GAAkB,EAAE,CAAC;IAChC,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,IAAI,GAAgB,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;QACnE,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM;YAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;QACxD,IAAI,CAAC,CAAC,QAAQ;YAAE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QAErC,IAAI,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;YAChD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,SAAS;QACX,CAAC;QACD,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC5D,IAAI,CAAC,QAAQ;YAAE,SAAS,CAAC,gEAAgE;QACzF,IAAI,CAAC,UAAU,GAAG,IAAA,oCAAiB,EAAC,QAAQ,EAAE,GAAG,CAA4B,CAAC;QAC9E,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,4EAA4E;AAC5E,SAAgB,cAAc,CAAC,KAAoB;IACjD,OAAO,IAAA,4BAAmB,EAAC,KAAkD,CAAC,CAAC;AACjF,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type Redis from 'ioredis';
2
+ export declare const DEFAULT_LOCK_PREFIX = "authz:lock:";
3
+ export declare const DEFAULT_LOCK_TTL_SEC = 10;
4
+ export declare const DEFAULT_BACKOFF_MS: number[];
5
+ export declare class DistributedLock {
6
+ private readonly redis;
7
+ private readonly prefix;
8
+ constructor(redis: Redis, prefix?: string);
9
+ /** SET key owner EX ttl NX → true if acquired. */
10
+ acquire(key: string, owner: string, ttlSec?: number): Promise<boolean>;
11
+ /** Release only if still owned (compare-and-delete, avoids deleting a re-acquired lock). */
12
+ release(key: string, owner: string): Promise<void>;
13
+ /**
14
+ * Lock loser path: poll `read` with backoff, returning the first non-null result.
15
+ * Returns null if all attempts are exhausted (→ caller falls back to a direct build).
16
+ */
17
+ pollForResult<T>(read: () => Promise<T | null>, backoffMs?: number[]): Promise<T | null>;
18
+ }
19
+ //# sourceMappingURL=distributed-lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"distributed-lock.d.ts","sourceRoot":"","sources":["../../src/snapshot/distributed-lock.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AAIjC,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AACjD,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,kBAAkB,UAAmB,CAAC;AAEnD,qBAAa,eAAe;IAExB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,KAAK,EAAE,KAAK,EACZ,MAAM,GAAE,MAA4B;IAGvD,kDAAkD;IAC5C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,SAAuB,GAAG,OAAO,CAAC,OAAO,CAAC;IAI1F,4FAA4F;IACtF,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMxD;;;OAGG;IACG,aAAa,CAAC,CAAC,EACnB,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,EAC7B,SAAS,GAAE,MAAM,EAAuB,GACvC,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;CAQrB"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DistributedLock = exports.DEFAULT_BACKOFF_MS = exports.DEFAULT_LOCK_TTL_SEC = exports.DEFAULT_LOCK_PREFIX = void 0;
4
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
5
+ exports.DEFAULT_LOCK_PREFIX = 'authz:lock:';
6
+ exports.DEFAULT_LOCK_TTL_SEC = 10;
7
+ exports.DEFAULT_BACKOFF_MS = [5, 15, 50, 150];
8
+ class DistributedLock {
9
+ constructor(redis, prefix = exports.DEFAULT_LOCK_PREFIX) {
10
+ this.redis = redis;
11
+ this.prefix = prefix;
12
+ }
13
+ /** SET key owner EX ttl NX → true if acquired. */
14
+ async acquire(key, owner, ttlSec = exports.DEFAULT_LOCK_TTL_SEC) {
15
+ return (await this.redis.set(this.prefix + key, owner, 'EX', ttlSec, 'NX')) === 'OK';
16
+ }
17
+ /** Release only if still owned (compare-and-delete, avoids deleting a re-acquired lock). */
18
+ async release(key, owner) {
19
+ const lua = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
20
+ await this.redis.eval(lua, 1, this.prefix + key, owner);
21
+ }
22
+ /**
23
+ * Lock loser path: poll `read` with backoff, returning the first non-null result.
24
+ * Returns null if all attempts are exhausted (→ caller falls back to a direct build).
25
+ */
26
+ async pollForResult(read, backoffMs = exports.DEFAULT_BACKOFF_MS) {
27
+ for (const wait of backoffMs) {
28
+ await sleep(wait);
29
+ const v = await read();
30
+ if (v != null)
31
+ return v;
32
+ }
33
+ return null;
34
+ }
35
+ }
36
+ exports.DistributedLock = DistributedLock;
37
+ //# sourceMappingURL=distributed-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"distributed-lock.js","sourceRoot":"","sources":["../../src/snapshot/distributed-lock.ts"],"names":[],"mappings":";;;AAKA,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAE7D,QAAA,mBAAmB,GAAG,aAAa,CAAC;AACpC,QAAA,oBAAoB,GAAG,EAAE,CAAC;AAC1B,QAAA,kBAAkB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;AAEnD,MAAa,eAAe;IAC1B,YACmB,KAAY,EACZ,SAAiB,2BAAmB;QADpC,UAAK,GAAL,KAAK,CAAO;QACZ,WAAM,GAAN,MAAM,CAA8B;IACpD,CAAC;IAEJ,kDAAkD;IAClD,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,KAAa,EAAE,MAAM,GAAG,4BAAoB;QACrE,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC;IACvF,CAAC;IAED,4FAA4F;IAC5F,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,KAAa;QACtC,MAAM,GAAG,GACP,mGAAmG,CAAC;QACtG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,GAAG,EAAE,KAAK,CAAC,CAAC;IAC1D,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,aAAa,CACjB,IAA6B,EAC7B,YAAsB,0BAAkB;QAExC,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;YAClB,MAAM,CAAC,GAAG,MAAM,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,IAAI,IAAI;gBAAE,OAAO,CAAC,CAAC;QAC1B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAjCD,0CAiCC"}
@@ -0,0 +1,7 @@
1
+ export * from './snapshot.envelope';
2
+ export * from './ability-builder';
3
+ export * from './perm-hash';
4
+ export * from './l1-cache';
5
+ export * from './distributed-lock';
6
+ export * from './snapshot.store';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/snapshot/index.ts"],"names":[],"mappings":"AAGA,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC"}
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ // @generazioneai/authz/snapshot — Step 4 permission snapshot primitives.
18
+ // Pure core (envelope, perm-hash) + Redis-backed pieces (store, lock) + in-process L1.
19
+ // The NestJS resolver/subscriber + skillID builder/emitter are wired in the services.
20
+ __exportStar(require("./snapshot.envelope"), exports);
21
+ __exportStar(require("./ability-builder"), exports);
22
+ __exportStar(require("./perm-hash"), exports);
23
+ __exportStar(require("./l1-cache"), exports);
24
+ __exportStar(require("./distributed-lock"), exports);
25
+ __exportStar(require("./snapshot.store"), exports);
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/snapshot/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,yEAAyE;AACzE,uFAAuF;AACvF,sFAAsF;AACtF,sDAAoC;AACpC,oDAAkC;AAClC,8CAA4B;AAC5B,6CAA2B;AAC3B,qDAAmC;AACnC,mDAAiC"}
@@ -0,0 +1,16 @@
1
+ import type { SnapshotEnvelope } from './snapshot.envelope';
2
+ export interface L1Options {
3
+ max?: number;
4
+ ttlMs?: number;
5
+ }
6
+ export declare class L1SnapshotCache {
7
+ private readonly cache;
8
+ constructor(opts?: L1Options);
9
+ private key;
10
+ get(userId: string, actingJuridicalId?: string): SnapshotEnvelope | undefined;
11
+ set(env: SnapshotEnvelope): void;
12
+ /** Evict one tenant entry, or ALL entries for a user when actingJuridicalId is omitted. */
13
+ evict(userId: string, actingJuridicalId?: string): number;
14
+ clear(): void;
15
+ }
16
+ //# sourceMappingURL=l1-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"l1-cache.d.ts","sourceRoot":"","sources":["../../src/snapshot/l1-cache.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;gBAE/C,IAAI,GAAE,SAAc;IAOhC,OAAO,CAAC,GAAG;IAIX,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAI7E,GAAG,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI;IAIhC,2FAA2F;IAC3F,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM;IAezD,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.L1SnapshotCache = void 0;
4
+ // Step 4 DEC-S4.5/10 — in-process L1 snapshot cache (TTL 30s = Redis TTL / 2, so an L1
5
+ // hit never outlives a revoke that already hit Redis). Keyed by (userId, actingJuridicalId).
6
+ const lru_cache_1 = require("lru-cache");
7
+ class L1SnapshotCache {
8
+ constructor(opts = {}) {
9
+ this.cache = new lru_cache_1.LRUCache({
10
+ max: opts.max ?? 2000,
11
+ ttl: opts.ttlMs ?? 30_000,
12
+ });
13
+ }
14
+ key(userId, actingJuridicalId) {
15
+ return `${userId}:${actingJuridicalId ?? ''}`;
16
+ }
17
+ get(userId, actingJuridicalId) {
18
+ return this.cache.get(this.key(userId, actingJuridicalId));
19
+ }
20
+ set(env) {
21
+ this.cache.set(this.key(env.userId, env.actingJuridicalId), env);
22
+ }
23
+ /** Evict one tenant entry, or ALL entries for a user when actingJuridicalId is omitted. */
24
+ evict(userId, actingJuridicalId) {
25
+ if (actingJuridicalId !== undefined) {
26
+ return this.cache.delete(this.key(userId, actingJuridicalId)) ? 1 : 0;
27
+ }
28
+ const prefix = `${userId}:`;
29
+ let n = 0;
30
+ for (const k of this.cache.keys()) {
31
+ if (k.startsWith(prefix)) {
32
+ this.cache.delete(k);
33
+ n++;
34
+ }
35
+ }
36
+ return n;
37
+ }
38
+ clear() {
39
+ this.cache.clear();
40
+ }
41
+ }
42
+ exports.L1SnapshotCache = L1SnapshotCache;
43
+ //# sourceMappingURL=l1-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"l1-cache.js","sourceRoot":"","sources":["../../src/snapshot/l1-cache.ts"],"names":[],"mappings":";;;AAAA,uFAAuF;AACvF,6FAA6F;AAC7F,yCAAqC;AAQrC,MAAa,eAAe;IAG1B,YAAY,OAAkB,EAAE;QAC9B,IAAI,CAAC,KAAK,GAAG,IAAI,oBAAQ,CAA2B;YAClD,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,IAAI;YACrB,GAAG,EAAE,IAAI,CAAC,KAAK,IAAI,MAAM;SAC1B,CAAC,CAAC;IACL,CAAC;IAEO,GAAG,CAAC,MAAc,EAAE,iBAA0B;QACpD,OAAO,GAAG,MAAM,IAAI,iBAAiB,IAAI,EAAE,EAAE,CAAC;IAChD,CAAC;IAED,GAAG,CAAC,MAAc,EAAE,iBAA0B;QAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC7D,CAAC;IAED,GAAG,CAAC,GAAqB;QACvB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,iBAAiB,CAAC,EAAE,GAAG,CAAC,CAAC;IACnE,CAAC;IAED,2FAA2F;IAC3F,KAAK,CAAC,MAAc,EAAE,iBAA0B;QAC9C,IAAI,iBAAiB,KAAK,SAAS,EAAE,CAAC;YACpC,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxE,CAAC;QACD,MAAM,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC;QAC5B,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YAClC,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBACzB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBACrB,CAAC,EAAE,CAAC;YACN,CAAC;QACH,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF;AAzCD,0CAyCC"}
@@ -0,0 +1,3 @@
1
+ import type { AbilityRule } from './snapshot.envelope';
2
+ export declare function computePermHash(rules: AbilityRule[]): string;
3
+ //# sourceMappingURL=perm-hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perm-hash.d.ts","sourceRoot":"","sources":["../../src/snapshot/perm-hash.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AA2BvD,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CAM5D"}
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.computePermHash = computePermHash;
7
+ // Step 4 DEC-S4.7/8/9 — deterministic permission hash (128-bit, 32 hex).
8
+ //
9
+ // Two users with the same roles but different tenantId get DIFFERENT permHashes
10
+ // (conditions are post-substitution → tenant-specific) — that is correct. The hash is
11
+ // cross-pod deterministic: rules are normalized + sorted, then json-stable-stringify'd.
12
+ const node_crypto_1 = require("node:crypto");
13
+ const json_stable_stringify_1 = __importDefault(require("json-stable-stringify"));
14
+ const asSortedArray = (v) => Array.isArray(v) ? [...v].sort() : v;
15
+ const ruleKey = (r) => `${JSON.stringify(r.subject)}|${JSON.stringify(r.action)}|${r.inverted ? 1 : 0}`;
16
+ /** Canonical, hash-relevant projection of a rule (excludes `reason`/metadata). */
17
+ function normalize(r) {
18
+ return {
19
+ action: asSortedArray(r.action),
20
+ subject: asSortedArray(r.subject),
21
+ fields: r.fields && r.fields.length ? [...r.fields].sort() : undefined,
22
+ conditions: r.conditions ?? undefined,
23
+ inverted: r.inverted ?? false,
24
+ };
25
+ }
26
+ function computePermHash(rules) {
27
+ const normalized = rules
28
+ .map(normalize)
29
+ .sort((a, b) => ruleKey(a).localeCompare(ruleKey(b)));
30
+ const canonical = (0, json_stable_stringify_1.default)(normalized) ?? '[]';
31
+ return (0, node_crypto_1.createHash)('sha256').update(canonical).digest('hex').slice(0, 32);
32
+ }
33
+ //# sourceMappingURL=perm-hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"perm-hash.js","sourceRoot":"","sources":["../../src/snapshot/perm-hash.ts"],"names":[],"mappings":";;;;;AAkCA,0CAMC;AAxCD,yEAAyE;AACzE,EAAE;AACF,gFAAgF;AAChF,sFAAsF;AACtF,wFAAwF;AACxF,6CAAyC;AACzC,kFAAoD;AAWpD,MAAM,aAAa,GAAG,CAAC,CAAoB,EAAqB,EAAE,CAChE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAEvC,MAAM,OAAO,GAAG,CAAC,CAAiB,EAAU,EAAE,CAC5C,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAEnF,kFAAkF;AAClF,SAAS,SAAS,CAAC,CAAc;IAC/B,OAAO;QACL,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;QAC/B,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC;QACjC,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS;QACtE,UAAU,EAAE,CAAC,CAAC,UAAU,IAAI,SAAS;QACrC,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,KAAK;KAC9B,CAAC;AACJ,CAAC;AAED,SAAgB,eAAe,CAAC,KAAoB;IAClD,MAAM,UAAU,GAAG,KAAK;SACrB,GAAG,CAAC,SAAS,CAAC;SACd,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,MAAM,SAAS,GAAG,IAAA,+BAAe,EAAC,UAAU,CAAC,IAAI,IAAI,CAAC;IACtD,OAAO,IAAA,wBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type { AccreditedAs, ConnectedEdges } from '../context/authz-context';
2
+ export declare const SNAPSHOT_SCHEMA_VERSION = 1;
3
+ /** A serialized CASL rule (post $ctx-substitution). Shape accepted by createPrismaAbility. */
4
+ export interface AbilityRule {
5
+ action: string | string[];
6
+ subject: string | string[];
7
+ /** Field-level allow-list (Step 1 fields[]). Empty/absent = all fields. */
8
+ fields?: string[];
9
+ /** Prisma WhereInput-shaped conditions (already substituted; no $ctx left). */
10
+ conditions?: Record<string, unknown>;
11
+ /** CASL `cannot` rule. */
12
+ inverted?: boolean;
13
+ /** Audit-only; excluded from permHash. */
14
+ reason?: string;
15
+ }
16
+ export interface SnapshotEnvelope {
17
+ schemaVersion: number;
18
+ snapId: string;
19
+ userId: string;
20
+ actingJuridicalId?: string;
21
+ /** sha256(canonical(sortedRules)) truncated to 32 hex (Step 4 DEC-S4.7/9). */
22
+ permHash: string;
23
+ /** epoch ms — drives refresh-ahead (DEC-S4.30). */
24
+ builtAt: number;
25
+ rules: AbilityRule[];
26
+ connected: ConnectedEdges;
27
+ accreditedAs: AccreditedAs;
28
+ /** Set when the rules blob was lz4-compressed (DEC-S4.27). Day-1: null. */
29
+ compressed?: 'lz4' | null;
30
+ }
31
+ /**
32
+ * Opaque snapshot id (DEC-S4.4): doesn't leak userId, idempotent per 1s build bucket.
33
+ * blake3 in the plan; sha256 here keeps zero extra deps (opacity is what matters).
34
+ */
35
+ export declare function computeSnapId(userId: string, actingJuridicalId: string | undefined, builtAtMs: number): string;
36
+ //# sourceMappingURL=snapshot.envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.envelope.d.ts","sourceRoot":"","sources":["../../src/snapshot/snapshot.envelope.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE7E,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,8FAA8F;AAC9F,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,SAAS,EAAE,cAAc,CAAC;IAC1B,YAAY,EAAE,YAAY,CAAC;IAC3B,2EAA2E;IAC3E,UAAU,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;CAC3B;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,iBAAiB,EAAE,MAAM,GAAG,SAAS,EACrC,SAAS,EAAE,MAAM,GAChB,MAAM,CAMR"}
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SNAPSHOT_SCHEMA_VERSION = void 0;
4
+ exports.computeSnapId = computeSnapId;
5
+ // Step 4 DEC-S4.1/3 — the permission snapshot envelope cached in Redis.
6
+ //
7
+ // skillID builds it (rules already $ctx-substituted), stores it under an opaque snapId;
8
+ // the gateway/downstream fetch it by snapId (carried in the Step 2 JWT `snap` claim) and
9
+ // rehydrate the CASL ability via createPrismaAbility(rules).
10
+ const node_crypto_1 = require("node:crypto");
11
+ exports.SNAPSHOT_SCHEMA_VERSION = 1;
12
+ /**
13
+ * Opaque snapshot id (DEC-S4.4): doesn't leak userId, idempotent per 1s build bucket.
14
+ * blake3 in the plan; sha256 here keeps zero extra deps (opacity is what matters).
15
+ */
16
+ function computeSnapId(userId, actingJuridicalId, builtAtMs) {
17
+ const bucket = Math.floor(builtAtMs / 1000);
18
+ return (0, node_crypto_1.createHash)('sha256')
19
+ .update(`${userId}:${actingJuridicalId ?? ''}:${bucket}`)
20
+ .digest('hex')
21
+ .slice(0, 22);
22
+ }
23
+ //# sourceMappingURL=snapshot.envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.envelope.js","sourceRoot":"","sources":["../../src/snapshot/snapshot.envelope.ts"],"names":[],"mappings":";;;AA4CA,sCAUC;AAtDD,wEAAwE;AACxE,EAAE;AACF,wFAAwF;AACxF,yFAAyF;AACzF,6DAA6D;AAC7D,6CAAyC;AAG5B,QAAA,uBAAuB,GAAG,CAAC,CAAC;AAgCzC;;;GAGG;AACH,SAAgB,aAAa,CAC3B,MAAc,EACd,iBAAqC,EACrC,SAAiB;IAEjB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IAC5C,OAAO,IAAA,wBAAU,EAAC,QAAQ,CAAC;SACxB,MAAM,CAAC,GAAG,MAAM,IAAI,iBAAiB,IAAI,EAAE,IAAI,MAAM,EAAE,CAAC;SACxD,MAAM,CAAC,KAAK,CAAC;SACb,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type Redis from 'ioredis';
2
+ import type { SnapshotEnvelope } from './snapshot.envelope';
3
+ export declare const SNAP_PREFIX = "authz:snap:";
4
+ export declare const CURRENT_PREFIX = "authz:user:";
5
+ export declare const ROLE_PREFIX = "authz:role:";
6
+ export declare const DEFAULT_SNAPSHOT_TTL_SEC = 60;
7
+ export declare class RedisSnapshotStore {
8
+ private readonly redis;
9
+ constructor(redis: Redis);
10
+ /** Store the envelope + point the user's `current` pointer at it (atomic pipeline). */
11
+ put(env: SnapshotEnvelope, ttlSec?: number): Promise<void>;
12
+ getBySnapId(snapId: string): Promise<SnapshotEnvelope | null>;
13
+ /** Resolve the current snapId for (user, tenant) then load the envelope (pipelined). */
14
+ getCurrent(userId: string, actingJur?: string): Promise<SnapshotEnvelope | null>;
15
+ indexRoleMember(tier: string, roleId: string, userId: string): Promise<void>;
16
+ usersForRole(tier: string, roleId: string): Promise<string[]>;
17
+ /** Evict every snapshot of a user across tenants (SCAN the `current` pointers). */
18
+ revokeUser(userId: string): Promise<number>;
19
+ /** Evict one (user, tenant) snapshot. */
20
+ revokeUserTenant(userId: string, actingJur: string): Promise<void>;
21
+ /** Cascade a role revoke to all its members (reverse-index lookup → per-user evict). */
22
+ revokeRole(tier: string, roleId: string): Promise<number>;
23
+ }
24
+ //# sourceMappingURL=snapshot.store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.store.d.ts","sourceRoot":"","sources":["../../src/snapshot/snapshot.store.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,eAAO,MAAM,WAAW,gBAAgB,CAAC;AACzC,eAAO,MAAM,cAAc,gBAAgB,CAAC;AAC5C,eAAO,MAAM,WAAW,gBAAgB,CAAC;AACzC,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAM3C,qBAAa,kBAAkB;IACjB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,KAAK;IAEzC,uFAAuF;IACjF,GAAG,CAAC,GAAG,EAAE,gBAAgB,EAAE,MAAM,SAA2B,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ5E,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAKnE,wFAAwF;IAClF,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAMhF,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAG5E,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAKnE,mFAAmF;IAC7E,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAmBjD,yCAAyC;IACnC,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASxE,wFAAwF;IAClF,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAMhE"}
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RedisSnapshotStore = exports.DEFAULT_SNAPSHOT_TTL_SEC = exports.ROLE_PREFIX = exports.CURRENT_PREFIX = exports.SNAP_PREFIX = void 0;
4
+ exports.SNAP_PREFIX = 'authz:snap:';
5
+ exports.CURRENT_PREFIX = 'authz:user:';
6
+ exports.ROLE_PREFIX = 'authz:role:';
7
+ exports.DEFAULT_SNAPSHOT_TTL_SEC = 60;
8
+ const currentKey = (userId, actingJur) => `${exports.CURRENT_PREFIX}${userId}:${actingJur ?? ''}:current`;
9
+ const roleKey = (tier, roleId) => `${exports.ROLE_PREFIX}${tier}:${roleId}:users`;
10
+ class RedisSnapshotStore {
11
+ constructor(redis) {
12
+ this.redis = redis;
13
+ }
14
+ /** Store the envelope + point the user's `current` pointer at it (atomic pipeline). */
15
+ async put(env, ttlSec = exports.DEFAULT_SNAPSHOT_TTL_SEC) {
16
+ await this.redis
17
+ .pipeline()
18
+ .set(exports.SNAP_PREFIX + env.snapId, JSON.stringify(env), 'EX', ttlSec)
19
+ .set(currentKey(env.userId, env.actingJuridicalId), env.snapId, 'EX', ttlSec)
20
+ .exec();
21
+ }
22
+ async getBySnapId(snapId) {
23
+ const raw = await this.redis.get(exports.SNAP_PREFIX + snapId);
24
+ return raw ? JSON.parse(raw) : null;
25
+ }
26
+ /** Resolve the current snapId for (user, tenant) then load the envelope (pipelined). */
27
+ async getCurrent(userId, actingJur) {
28
+ const snapId = await this.redis.get(currentKey(userId, actingJur));
29
+ return snapId ? this.getBySnapId(snapId) : null;
30
+ }
31
+ // ---- reverse-index (role-cascade revocation, DEC-S4.15) --------------------
32
+ async indexRoleMember(tier, roleId, userId) {
33
+ await this.redis.sadd(roleKey(tier, roleId), userId);
34
+ }
35
+ async usersForRole(tier, roleId) {
36
+ return this.redis.smembers(roleKey(tier, roleId));
37
+ }
38
+ // ---- revocation (DEC-S4.13) ------------------------------------------------
39
+ /** Evict every snapshot of a user across tenants (SCAN the `current` pointers). */
40
+ async revokeUser(userId) {
41
+ const pattern = `${exports.CURRENT_PREFIX}${userId}:*:current`;
42
+ let cursor = '0';
43
+ let evicted = 0;
44
+ do {
45
+ const [next, keys] = await this.redis.scan(cursor, 'MATCH', pattern, 'COUNT', 200);
46
+ cursor = next;
47
+ if (keys.length) {
48
+ const snapIds = (await this.redis.mget(...keys)).filter(Boolean);
49
+ const pipe = this.redis.pipeline();
50
+ keys.forEach((k) => pipe.del(k));
51
+ snapIds.forEach((id) => pipe.del(exports.SNAP_PREFIX + id));
52
+ await pipe.exec();
53
+ evicted += keys.length;
54
+ }
55
+ } while (cursor !== '0');
56
+ return evicted;
57
+ }
58
+ /** Evict one (user, tenant) snapshot. */
59
+ async revokeUserTenant(userId, actingJur) {
60
+ const ck = currentKey(userId, actingJur);
61
+ const snapId = await this.redis.get(ck);
62
+ const pipe = this.redis.pipeline();
63
+ pipe.del(ck);
64
+ if (snapId)
65
+ pipe.del(exports.SNAP_PREFIX + snapId);
66
+ await pipe.exec();
67
+ }
68
+ /** Cascade a role revoke to all its members (reverse-index lookup → per-user evict). */
69
+ async revokeRole(tier, roleId) {
70
+ const users = await this.usersForRole(tier, roleId);
71
+ let n = 0;
72
+ for (const u of users)
73
+ n += await this.revokeUser(u);
74
+ return n;
75
+ }
76
+ }
77
+ exports.RedisSnapshotStore = RedisSnapshotStore;
78
+ //# sourceMappingURL=snapshot.store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.store.js","sourceRoot":"","sources":["../../src/snapshot/snapshot.store.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@generazioneai/authz",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Runtime authz + autoquery for Skillera microservices",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -44,6 +44,14 @@
44
44
  "./nest": {
45
45
  "types": "./dist/nest/index.d.ts",
46
46
  "default": "./dist/nest/index.js"
47
+ },
48
+ "./snapshot": {
49
+ "types": "./dist/snapshot/index.d.ts",
50
+ "default": "./dist/snapshot/index.js"
51
+ },
52
+ "./enforce": {
53
+ "types": "./dist/enforce/index.d.ts",
54
+ "default": "./dist/enforce/index.js"
47
55
  }
48
56
  },
49
57
  "typesVersions": {
@@ -56,12 +64,18 @@
56
64
  ],
57
65
  "internal": [
58
66
  "dist/internal/index.d.ts"
67
+ ],
68
+ "snapshot": [
69
+ "dist/snapshot/index.d.ts"
70
+ ],
71
+ "enforce": [
72
+ "dist/enforce/index.d.ts"
59
73
  ]
60
74
  }
61
75
  },
62
76
  "peerDependencies": {
63
77
  "@casl/ability": "^6.7.0",
64
- "@casl/prisma": "^1.5.0 || ^2.0.0",
78
+ "@casl/prisma": "^1.5.0",
65
79
  "@nestjs/common": "^11.0.0",
66
80
  "@nestjs/core": "^11.0.0",
67
81
  "@nestjs/microservices": "^11.0.0",
@@ -70,16 +84,23 @@
70
84
  "peerDependenciesMeta": {
71
85
  "@prisma/client": {
72
86
  "optional": true
87
+ },
88
+ "@casl/ability": {
89
+ "optional": true
90
+ },
91
+ "@casl/prisma": {
92
+ "optional": true
73
93
  }
74
94
  },
75
95
  "dependencies": {
76
- "jose": "^6.0.10",
77
96
  "ioredis": "^5.4.0",
78
- "json-stable-stringify": "^1.1.1"
97
+ "jose": "^6.0.10",
98
+ "json-stable-stringify": "^1.1.1",
99
+ "lru-cache": "^10.4.0"
79
100
  },
80
101
  "devDependencies": {
81
102
  "@casl/ability": "^6.7.3",
82
- "@casl/prisma": "^1.5.0",
103
+ "@casl/prisma": "^1.6.2",
83
104
  "@nestjs/core": "^11.1.24",
84
105
  "@nestjs/microservices": "^11.1.24",
85
106
  "@types/json-stable-stringify": "^1.0.36",