@spinajs/rbac 2.0.525 → 2.0.527

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.
@@ -15,8 +15,21 @@ export type PermissionGrantScope = 'any' | 'own' | 'none';
15
15
  * `usePermission()` anywhere else share one instance safely.
16
16
  */
17
17
  export declare abstract class PermissionService {
18
- /** Rbac resource name this service guards — same string the grants config uses. */
19
- protected abstract readonly Resource: string;
18
+ /**
19
+ * The rbac resource this service guards. A class constructor is the preferred form — its NAME
20
+ * is the accesscontrol grant key, resolved directly with no descriptor or metadata lookup, and
21
+ * ANY class qualifies (an ORM model, a DTO, a plain feature class). A string stays legal for
22
+ * grant keys that have no class behind them (config-only pseudo-resources).
23
+ */
24
+ protected abstract readonly Resource: Class<unknown> | string;
25
+ /**
26
+ * The grant key {@link Resource} resolves to, self-checked against the registered grants:
27
+ * with a class as the resource the class NAME is the config contract, so a renamed class
28
+ * would otherwise fail closed silently (probeGrant answers false for a key accesscontrol
29
+ * has never seen). Checked here, it fails loud at first use instead — a programming error,
30
+ * not a refusal. Skipped when no AccessControl is registered; the probe itself throws then.
31
+ */
32
+ protected resourceName(): string;
20
33
  /**
21
34
  * The acting user: explicit argument wins, else the rbac AsyncLocalStorage store.
22
35
  * No user anywhere throws — a permission decision must never silently pass without
@@ -41,10 +54,70 @@ export declare abstract class PermissionService {
41
54
  * else in the store kept.
42
55
  */
43
56
  protected withUser<T>(user: User, fn: () => T | Promise<T>): Promise<T>;
57
+ /**
58
+ * A fluent, awaitable sequence of this service's own rule checks — see {@link RuleChain}.
59
+ * Composite rules build out of the simple ones without imperative glue:
60
+ *
61
+ * await this.chain().assert('update', entry, user).assertFileCapacity(entry);
62
+ *
63
+ * Public so a domain service can compose the rules it consumes the same way:
64
+ *
65
+ * await this.permissions.chain().assert('update', entry, actor).assertFileCapacity(entry);
66
+ */
67
+ chain(): RuleChain<this>;
44
68
  protected noPermissionError(user: User, verb: PermissionVerb): Error;
45
69
  protected noUserError(): Error;
46
70
  private storage;
47
71
  }
72
+ /**
73
+ * Model-aware permission service: the generic verb gate computed from {@link Resource} plus the
74
+ * accesscontrol grants, so a concrete domain service declares only what config cannot state —
75
+ * what "own" MEANS for its resource — plus its typed errors and genuine business rules.
76
+ *
77
+ * `TModel` is the ownership CARRIER, not necessarily a persisted row: anything `owns` can decide
78
+ * from. The Resource class doubles as the grant key and as the compile-time subject type, so the
79
+ * declaration cannot drift from what `assert` accepts.
80
+ *
81
+ * `read` deliberately has no special treatment here, but domain convention is to not gate reads
82
+ * through `assert` at all: a refused read should answer an empty list or 404 from the scoped
83
+ * query layer, never a 403.
84
+ */
85
+ export declare abstract class ResourceRules<TModel> extends PermissionService {
86
+ protected abstract readonly Resource: Class<TModel>;
87
+ /**
88
+ * THE one domain fact the grants config cannot express: does this user own this subject.
89
+ * Runs only when the grant projects to `own` scope — an `any` grant never calls it.
90
+ */
91
+ protected abstract owns(user: User, subject: TModel): Promise<boolean> | boolean;
92
+ /**
93
+ * The generic gate: grant first (refusing `none` with {@link noPermissionError}), ownership
94
+ * second when the grant is `own`-scoped. A missing subject under `own` scope refuses too —
95
+ * ownership of nothing is undecidable, and an undecidable permission never passes. Verbs on
96
+ * the collection (a `create:any` insert) pass with no subject because scope is `any`.
97
+ */
98
+ assert(verb: PermissionVerb, subject?: TModel, user?: User): Promise<void>;
99
+ /** Override hook for typed domain errors, like {@link noPermissionError}. */
100
+ protected notOwnedError(user: User, _subject: TModel | undefined): Error;
101
+ }
102
+ /**
103
+ * A fluent, awaitable sequence of rule checks — what `chain()` answers.
104
+ *
105
+ * Every method of the underlying service appears here with the same arguments but answering
106
+ * the chain again (meant for the assert-style rules; a queued step's own return value is
107
+ * discarded), so composite rules read as one sentence:
108
+ *
109
+ * await this.chain().assert('update', entry, user).assertFileCapacity(entry);
110
+ *
111
+ * Nothing runs until the chain is awaited; then the steps run IN CALL ORDER, sequentially and
112
+ * fail-fast — the first throw rejects the chain and later steps never execute, so a cheap grant
113
+ * check placed first still spares the DB lookups behind it. Awaiting the same chain twice runs
114
+ * it once.
115
+ */
116
+ export type RuleChain<T> = PromiseLike<void> & {
117
+ [K in keyof T]: T[K] extends (...args: infer A) => unknown ? (...args: A) => RuleChain<T> : never;
118
+ };
119
+ /** Builds a {@link RuleChain} over any rules object — the mechanics behind `chain()`. */
120
+ export declare function ruleChain<T extends object>(target: T): RuleChain<T>;
48
121
  /**
49
122
  * Retrieves a permission service from DI — sugar for domain services and plain
50
123
  * functions down the callstack, so code reads as intent
@@ -1 +1 @@
1
- {"version":3,"file":"permission-service.d.ts","sourceRoot":"","sources":["../../src/permission-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,UAAU,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,KAAK,EAAM,MAAM,aAAa,CAAC;AAGxC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AACrE,MAAM,MAAM,oBAAoB,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,8BAAsB,iBAAiB;IACrC,mFAAmF;IACnF,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE7C;;;;OAIG;IACH,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAWhC;IAED,8FAA8F;IAC9F,SAAS,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAE7C;IAED,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,GAAG,oBAAoB,CAU3E;IAED,kFAAkF;IAClF,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAE1D;IAED,wFAAwF;IACxF,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAA;KAAE,CAS7F;IAED;;;;;;OAMG;IACH,UAAgB,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAS5E;IAED,SAAS,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,GAAG,KAAK,CAEnE;IAED,SAAS,CAAC,WAAW,IAAI,KAAK,CAE7B;IAED,OAAO,CAAC,OAAO;CAGhB;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,iBAAiB,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAM5E;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAc5G;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,EAAE,CAK1G"}
1
+ {"version":3,"file":"permission-service.d.ts","sourceRoot":"","sources":["../../src/permission-service.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PermissionService = void 0;
3
+ exports.ResourceRules = exports.PermissionService = void 0;
4
+ exports.ruleChain = ruleChain;
4
5
  exports.usePermission = usePermission;
5
6
  exports.probeGrant = probeGrant;
6
7
  exports.effectiveRoles = effectiveRoles;
@@ -20,6 +21,21 @@ const exceptions_1 = require("@spinajs/exceptions");
20
21
  * `usePermission()` anywhere else share one instance safely.
21
22
  */
22
23
  class PermissionService {
24
+ /**
25
+ * The grant key {@link Resource} resolves to, self-checked against the registered grants:
26
+ * with a class as the resource the class NAME is the config contract, so a renamed class
27
+ * would otherwise fail closed silently (probeGrant answers false for a key accesscontrol
28
+ * has never seen). Checked here, it fails loud at first use instead — a programming error,
29
+ * not a refusal. Skipped when no AccessControl is registered; the probe itself throws then.
30
+ */
31
+ resourceName() {
32
+ const name = typeof this.Resource === 'function' ? this.Resource.name : this.Resource;
33
+ const ac = di_1.DI.get('AccessControl');
34
+ if (ac && !ac.hasResource(name)) {
35
+ throw new Error(`rbac resource '${name}' (guarded by ${this.constructor.name}) is not present in the AccessControl grants — renamed class or missing grants config?`);
36
+ }
37
+ return name;
38
+ }
23
39
  /**
24
40
  * The acting user: explicit argument wins, else the rbac AsyncLocalStorage store.
25
41
  * No user anywhere throws — a permission decision must never silently pass without
@@ -51,7 +67,7 @@ class PermissionService {
51
67
  }
52
68
  /** Single guarded probe against this.Resource — an unknown role answers false. */
53
69
  can(roles, permission) {
54
- return probeGrant(roles, permission, this.Resource)?.granted ?? false;
70
+ return probeGrant(roles, permission, this.resourceName())?.granted ?? false;
55
71
  }
56
72
  /** Resolve acting user + scope, refusing `none` with the (overridable) domain error. */
57
73
  assertGrant(verb, user) {
@@ -77,8 +93,21 @@ class PermissionService {
77
93
  }
78
94
  return storage.run({ ...parent, User: user, ActiveRole: undefined }, async () => fn());
79
95
  }
96
+ /**
97
+ * A fluent, awaitable sequence of this service's own rule checks — see {@link RuleChain}.
98
+ * Composite rules build out of the simple ones without imperative glue:
99
+ *
100
+ * await this.chain().assert('update', entry, user).assertFileCapacity(entry);
101
+ *
102
+ * Public so a domain service can compose the rules it consumes the same way:
103
+ *
104
+ * await this.permissions.chain().assert('update', entry, actor).assertFileCapacity(entry);
105
+ */
106
+ chain() {
107
+ return ruleChain(this);
108
+ }
80
109
  noPermissionError(user, verb) {
81
- return new exceptions_1.Forbidden(`user ${user.Id} has no ${verb} grant for resource ${this.Resource}`);
110
+ return new exceptions_1.Forbidden(`user ${user.Id} has no ${verb} grant for resource ${this.resourceName()}`);
82
111
  }
83
112
  noUserError() {
84
113
  return new exceptions_1.Forbidden('no acting user: none was given and none is present in the execution context');
@@ -88,6 +117,66 @@ class PermissionService {
88
117
  }
89
118
  }
90
119
  exports.PermissionService = PermissionService;
120
+ /**
121
+ * Model-aware permission service: the generic verb gate computed from {@link Resource} plus the
122
+ * accesscontrol grants, so a concrete domain service declares only what config cannot state —
123
+ * what "own" MEANS for its resource — plus its typed errors and genuine business rules.
124
+ *
125
+ * `TModel` is the ownership CARRIER, not necessarily a persisted row: anything `owns` can decide
126
+ * from. The Resource class doubles as the grant key and as the compile-time subject type, so the
127
+ * declaration cannot drift from what `assert` accepts.
128
+ *
129
+ * `read` deliberately has no special treatment here, but domain convention is to not gate reads
130
+ * through `assert` at all: a refused read should answer an empty list or 404 from the scoped
131
+ * query layer, never a 403.
132
+ */
133
+ class ResourceRules extends PermissionService {
134
+ /**
135
+ * The generic gate: grant first (refusing `none` with {@link noPermissionError}), ownership
136
+ * second when the grant is `own`-scoped. A missing subject under `own` scope refuses too —
137
+ * ownership of nothing is undecidable, and an undecidable permission never passes. Verbs on
138
+ * the collection (a `create:any` insert) pass with no subject because scope is `any`.
139
+ */
140
+ async assert(verb, subject, user) {
141
+ const { user: acting, scope } = this.assertGrant(verb, user);
142
+ if (scope !== 'own') {
143
+ return;
144
+ }
145
+ if (subject === undefined || !(await this.owns(acting, subject))) {
146
+ throw this.notOwnedError(acting, subject);
147
+ }
148
+ }
149
+ /** Override hook for typed domain errors, like {@link noPermissionError}. */
150
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
151
+ notOwnedError(user, _subject) {
152
+ return new exceptions_1.Forbidden(`user ${user.Id} does not own the ${this.resourceName()} subject of this action`);
153
+ }
154
+ }
155
+ exports.ResourceRules = ResourceRules;
156
+ /** Builds a {@link RuleChain} over any rules object — the mechanics behind `chain()`. */
157
+ function ruleChain(target) {
158
+ const steps = [];
159
+ let run;
160
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
161
+ const proxy = new Proxy({}, {
162
+ get(_, prop) {
163
+ if (prop === 'then') {
164
+ run ??= (async () => {
165
+ for (const step of steps) {
166
+ await step();
167
+ }
168
+ })();
169
+ return run.then.bind(run);
170
+ }
171
+ return (...args) => {
172
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-call
173
+ steps.push(() => target[prop](...args));
174
+ return proxy;
175
+ };
176
+ },
177
+ });
178
+ return proxy;
179
+ }
91
180
  /**
92
181
  * Retrieves a permission service from DI — sugar for domain services and plain
93
182
  * functions down the callstack, so code reads as intent
@@ -1 +1 @@
1
- {"version":3,"file":"permission-service.js","sourceRoot":"","sources":["../../src/permission-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"permission-service.js","sourceRoot":"","sources":["../../src/permission-service.ts"],"names":[],"mappings":"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"}
@@ -15,8 +15,21 @@ export type PermissionGrantScope = 'any' | 'own' | 'none';
15
15
  * `usePermission()` anywhere else share one instance safely.
16
16
  */
17
17
  export declare abstract class PermissionService {
18
- /** Rbac resource name this service guards — same string the grants config uses. */
19
- protected abstract readonly Resource: string;
18
+ /**
19
+ * The rbac resource this service guards. A class constructor is the preferred form — its NAME
20
+ * is the accesscontrol grant key, resolved directly with no descriptor or metadata lookup, and
21
+ * ANY class qualifies (an ORM model, a DTO, a plain feature class). A string stays legal for
22
+ * grant keys that have no class behind them (config-only pseudo-resources).
23
+ */
24
+ protected abstract readonly Resource: Class<unknown> | string;
25
+ /**
26
+ * The grant key {@link Resource} resolves to, self-checked against the registered grants:
27
+ * with a class as the resource the class NAME is the config contract, so a renamed class
28
+ * would otherwise fail closed silently (probeGrant answers false for a key accesscontrol
29
+ * has never seen). Checked here, it fails loud at first use instead — a programming error,
30
+ * not a refusal. Skipped when no AccessControl is registered; the probe itself throws then.
31
+ */
32
+ protected resourceName(): string;
20
33
  /**
21
34
  * The acting user: explicit argument wins, else the rbac AsyncLocalStorage store.
22
35
  * No user anywhere throws — a permission decision must never silently pass without
@@ -41,10 +54,70 @@ export declare abstract class PermissionService {
41
54
  * else in the store kept.
42
55
  */
43
56
  protected withUser<T>(user: User, fn: () => T | Promise<T>): Promise<T>;
57
+ /**
58
+ * A fluent, awaitable sequence of this service's own rule checks — see {@link RuleChain}.
59
+ * Composite rules build out of the simple ones without imperative glue:
60
+ *
61
+ * await this.chain().assert('update', entry, user).assertFileCapacity(entry);
62
+ *
63
+ * Public so a domain service can compose the rules it consumes the same way:
64
+ *
65
+ * await this.permissions.chain().assert('update', entry, actor).assertFileCapacity(entry);
66
+ */
67
+ chain(): RuleChain<this>;
44
68
  protected noPermissionError(user: User, verb: PermissionVerb): Error;
45
69
  protected noUserError(): Error;
46
70
  private storage;
47
71
  }
72
+ /**
73
+ * Model-aware permission service: the generic verb gate computed from {@link Resource} plus the
74
+ * accesscontrol grants, so a concrete domain service declares only what config cannot state —
75
+ * what "own" MEANS for its resource — plus its typed errors and genuine business rules.
76
+ *
77
+ * `TModel` is the ownership CARRIER, not necessarily a persisted row: anything `owns` can decide
78
+ * from. The Resource class doubles as the grant key and as the compile-time subject type, so the
79
+ * declaration cannot drift from what `assert` accepts.
80
+ *
81
+ * `read` deliberately has no special treatment here, but domain convention is to not gate reads
82
+ * through `assert` at all: a refused read should answer an empty list or 404 from the scoped
83
+ * query layer, never a 403.
84
+ */
85
+ export declare abstract class ResourceRules<TModel> extends PermissionService {
86
+ protected abstract readonly Resource: Class<TModel>;
87
+ /**
88
+ * THE one domain fact the grants config cannot express: does this user own this subject.
89
+ * Runs only when the grant projects to `own` scope — an `any` grant never calls it.
90
+ */
91
+ protected abstract owns(user: User, subject: TModel): Promise<boolean> | boolean;
92
+ /**
93
+ * The generic gate: grant first (refusing `none` with {@link noPermissionError}), ownership
94
+ * second when the grant is `own`-scoped. A missing subject under `own` scope refuses too —
95
+ * ownership of nothing is undecidable, and an undecidable permission never passes. Verbs on
96
+ * the collection (a `create:any` insert) pass with no subject because scope is `any`.
97
+ */
98
+ assert(verb: PermissionVerb, subject?: TModel, user?: User): Promise<void>;
99
+ /** Override hook for typed domain errors, like {@link noPermissionError}. */
100
+ protected notOwnedError(user: User, _subject: TModel | undefined): Error;
101
+ }
102
+ /**
103
+ * A fluent, awaitable sequence of rule checks — what `chain()` answers.
104
+ *
105
+ * Every method of the underlying service appears here with the same arguments but answering
106
+ * the chain again (meant for the assert-style rules; a queued step's own return value is
107
+ * discarded), so composite rules read as one sentence:
108
+ *
109
+ * await this.chain().assert('update', entry, user).assertFileCapacity(entry);
110
+ *
111
+ * Nothing runs until the chain is awaited; then the steps run IN CALL ORDER, sequentially and
112
+ * fail-fast — the first throw rejects the chain and later steps never execute, so a cheap grant
113
+ * check placed first still spares the DB lookups behind it. Awaiting the same chain twice runs
114
+ * it once.
115
+ */
116
+ export type RuleChain<T> = PromiseLike<void> & {
117
+ [K in keyof T]: T[K] extends (...args: infer A) => unknown ? (...args: A) => RuleChain<T> : never;
118
+ };
119
+ /** Builds a {@link RuleChain} over any rules object — the mechanics behind `chain()`. */
120
+ export declare function ruleChain<T extends object>(target: T): RuleChain<T>;
48
121
  /**
49
122
  * Retrieves a permission service from DI — sugar for domain services and plain
50
123
  * functions down the callstack, so code reads as intent
@@ -1 +1 @@
1
- {"version":3,"file":"permission-service.d.ts","sourceRoot":"","sources":["../../src/permission-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,UAAU,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,KAAK,EAAM,MAAM,aAAa,CAAC;AAGxC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AACrE,MAAM,MAAM,oBAAoB,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,8BAAsB,iBAAiB;IACrC,mFAAmF;IACnF,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE7C;;;;OAIG;IACH,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAWhC;IAED,8FAA8F;IAC9F,SAAS,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAE7C;IAED,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,GAAG,oBAAoB,CAU3E;IAED,kFAAkF;IAClF,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAE1D;IAED,wFAAwF;IACxF,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAA;KAAE,CAS7F;IAED;;;;;;OAMG;IACH,UAAgB,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAS5E;IAED,SAAS,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,GAAG,KAAK,CAEnE;IAED,SAAS,CAAC,WAAW,IAAI,KAAK,CAE7B;IAED,OAAO,CAAC,OAAO;CAGhB;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,iBAAiB,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAM5E;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAc5G;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,EAAE,CAK1G"}
1
+ {"version":3,"file":"permission-service.d.ts","sourceRoot":"","sources":["../../src/permission-service.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,21 @@ import { Forbidden } from '@spinajs/exceptions';
14
14
  * `usePermission()` anywhere else share one instance safely.
15
15
  */
16
16
  export class PermissionService {
17
+ /**
18
+ * The grant key {@link Resource} resolves to, self-checked against the registered grants:
19
+ * with a class as the resource the class NAME is the config contract, so a renamed class
20
+ * would otherwise fail closed silently (probeGrant answers false for a key accesscontrol
21
+ * has never seen). Checked here, it fails loud at first use instead — a programming error,
22
+ * not a refusal. Skipped when no AccessControl is registered; the probe itself throws then.
23
+ */
24
+ resourceName() {
25
+ const name = typeof this.Resource === 'function' ? this.Resource.name : this.Resource;
26
+ const ac = DI.get('AccessControl');
27
+ if (ac && !ac.hasResource(name)) {
28
+ throw new Error(`rbac resource '${name}' (guarded by ${this.constructor.name}) is not present in the AccessControl grants — renamed class or missing grants config?`);
29
+ }
30
+ return name;
31
+ }
17
32
  /**
18
33
  * The acting user: explicit argument wins, else the rbac AsyncLocalStorage store.
19
34
  * No user anywhere throws — a permission decision must never silently pass without
@@ -45,7 +60,7 @@ export class PermissionService {
45
60
  }
46
61
  /** Single guarded probe against this.Resource — an unknown role answers false. */
47
62
  can(roles, permission) {
48
- return probeGrant(roles, permission, this.Resource)?.granted ?? false;
63
+ return probeGrant(roles, permission, this.resourceName())?.granted ?? false;
49
64
  }
50
65
  /** Resolve acting user + scope, refusing `none` with the (overridable) domain error. */
51
66
  assertGrant(verb, user) {
@@ -71,8 +86,21 @@ export class PermissionService {
71
86
  }
72
87
  return storage.run({ ...parent, User: user, ActiveRole: undefined }, async () => fn());
73
88
  }
89
+ /**
90
+ * A fluent, awaitable sequence of this service's own rule checks — see {@link RuleChain}.
91
+ * Composite rules build out of the simple ones without imperative glue:
92
+ *
93
+ * await this.chain().assert('update', entry, user).assertFileCapacity(entry);
94
+ *
95
+ * Public so a domain service can compose the rules it consumes the same way:
96
+ *
97
+ * await this.permissions.chain().assert('update', entry, actor).assertFileCapacity(entry);
98
+ */
99
+ chain() {
100
+ return ruleChain(this);
101
+ }
74
102
  noPermissionError(user, verb) {
75
- return new Forbidden(`user ${user.Id} has no ${verb} grant for resource ${this.Resource}`);
103
+ return new Forbidden(`user ${user.Id} has no ${verb} grant for resource ${this.resourceName()}`);
76
104
  }
77
105
  noUserError() {
78
106
  return new Forbidden('no acting user: none was given and none is present in the execution context');
@@ -81,6 +109,65 @@ export class PermissionService {
81
109
  return DI.resolve(AsyncLocalStorage);
82
110
  }
83
111
  }
112
+ /**
113
+ * Model-aware permission service: the generic verb gate computed from {@link Resource} plus the
114
+ * accesscontrol grants, so a concrete domain service declares only what config cannot state —
115
+ * what "own" MEANS for its resource — plus its typed errors and genuine business rules.
116
+ *
117
+ * `TModel` is the ownership CARRIER, not necessarily a persisted row: anything `owns` can decide
118
+ * from. The Resource class doubles as the grant key and as the compile-time subject type, so the
119
+ * declaration cannot drift from what `assert` accepts.
120
+ *
121
+ * `read` deliberately has no special treatment here, but domain convention is to not gate reads
122
+ * through `assert` at all: a refused read should answer an empty list or 404 from the scoped
123
+ * query layer, never a 403.
124
+ */
125
+ export class ResourceRules extends PermissionService {
126
+ /**
127
+ * The generic gate: grant first (refusing `none` with {@link noPermissionError}), ownership
128
+ * second when the grant is `own`-scoped. A missing subject under `own` scope refuses too —
129
+ * ownership of nothing is undecidable, and an undecidable permission never passes. Verbs on
130
+ * the collection (a `create:any` insert) pass with no subject because scope is `any`.
131
+ */
132
+ async assert(verb, subject, user) {
133
+ const { user: acting, scope } = this.assertGrant(verb, user);
134
+ if (scope !== 'own') {
135
+ return;
136
+ }
137
+ if (subject === undefined || !(await this.owns(acting, subject))) {
138
+ throw this.notOwnedError(acting, subject);
139
+ }
140
+ }
141
+ /** Override hook for typed domain errors, like {@link noPermissionError}. */
142
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
143
+ notOwnedError(user, _subject) {
144
+ return new Forbidden(`user ${user.Id} does not own the ${this.resourceName()} subject of this action`);
145
+ }
146
+ }
147
+ /** Builds a {@link RuleChain} over any rules object — the mechanics behind `chain()`. */
148
+ export function ruleChain(target) {
149
+ const steps = [];
150
+ let run;
151
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
152
+ const proxy = new Proxy({}, {
153
+ get(_, prop) {
154
+ if (prop === 'then') {
155
+ run ??= (async () => {
156
+ for (const step of steps) {
157
+ await step();
158
+ }
159
+ })();
160
+ return run.then.bind(run);
161
+ }
162
+ return (...args) => {
163
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-call
164
+ steps.push(() => target[prop](...args));
165
+ return proxy;
166
+ };
167
+ },
168
+ });
169
+ return proxy;
170
+ }
84
171
  /**
85
172
  * Retrieves a permission service from DI — sugar for domain services and plain
86
173
  * functions down the callstack, so code reads as intent
@@ -1 +1 @@
1
- {"version":3,"file":"permission-service.js","sourceRoot":"","sources":["../../src/permission-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"permission-service.js","sourceRoot":"","sources":["../../src/permission-service.ts"],"names":[],"mappings":"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"}