@cqrs-ddd/pipeline-casl 0.5.1 → 0.6.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.
@@ -1,4 +1,4 @@
1
- import { IPipelineBehavior, IPipelineContext, NextDelegate, PIPELINE_BEHAVIOR_ID } from '@cqrs-ddd/pipeline';
1
+ import { IPipelineBehavior, type IPipelineBehaviorContract, IPipelineContext, NextDelegate, PIPELINE_BEHAVIOR_CONTRACT, PIPELINE_BEHAVIOR_ID } from '@cqrs-ddd/pipeline';
2
2
  import type { ICaslPermissionSource } from './interfaces/permission-source.interface.js';
3
3
  import type { AbilityRequirement } from './types/casl.types.js';
4
4
  /** Stable behavior identity; cache and idempotency order themselves after it. */
@@ -16,11 +16,18 @@ export interface CaslBehaviorOptions {
16
16
  * security-scoped keys, and throws `UnauthorizedActionException` unless every
17
17
  * requirement passes. An unauthenticated caller (`null` input) is denied.
18
18
  * Errors from the source or from malformed rules propagate unchanged.
19
- * A handler without `rules` runs without loading permissions.
19
+ * Placed globally, it passes a handler that declares no `rules` without loading
20
+ * permissions; a handler that places it itself must declare them, which the
21
+ * pipeline diagnostics check.
20
22
  */
21
23
  export declare class CaslBehavior implements IPipelineBehavior {
22
24
  private readonly source;
23
25
  static readonly [PIPELINE_BEHAVIOR_ID] = "@cqrs-ddd/pipeline-casl:CaslBehavior";
26
+ /**
27
+ * A call-site declaration must name what it requires: without rules the
28
+ * handler would run unchecked. A global declaration may stay without rules.
29
+ */
30
+ static readonly [PIPELINE_BEHAVIOR_CONTRACT]: IPipelineBehaviorContract;
24
31
  constructor(source: ICaslPermissionSource);
25
32
  handle(context: IPipelineContext, next: NextDelegate): Promise<unknown>;
26
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"casl.behavior.d.ts","sourceRoot":"","sources":["../src/casl.behavior.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,oBAAoB,EACrB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACzF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,yCAAyC,CAAC;AAEvE,kEAAkE;AAClE,MAAM,WAAW,mBAAmB;IAClC,uDAAuD;IACvD,KAAK,EAAE,SAAS,CAAC,kBAAkB,EAAE,GAAG,kBAAkB,EAAE,CAAC,CAAC;CAC/D;AAED;;;;;;;;;GASG;AACH,qBAAa,YAAa,YAAW,iBAAiB;IAGxC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CAAC,0CAAoB;IAE1D,YAA6B,MAAM,EAAE,qBAAqB,EAMzD;IAEK,MAAM,CACV,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,OAAO,CAAC,CAgClB;CACF"}
1
+ {"version":3,"file":"casl.behavior.d.ts","sourceRoot":"","sources":["../src/casl.behavior.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,iBAAiB,EACjB,KAAK,yBAAyB,EAC9B,gBAAgB,EAChB,YAAY,EACZ,0BAA0B,EAC1B,oBAAoB,EACrB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AACzF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,yCAAyC,CAAC;AAEvE,kEAAkE;AAClE,MAAM,WAAW,mBAAmB;IAClC,uDAAuD;IACvD,KAAK,EAAE,SAAS,CAAC,kBAAkB,EAAE,GAAG,kBAAkB,EAAE,CAAC,CAAC;CAC/D;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,YAAa,YAAW,iBAAiB;IAwBxC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAvBnC,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CAAC,0CAAoB;IAE1D;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,CAAC,0BAA0B,CAAC,EAAE,yBAAyB,CAerE;IAEF,YAA6B,MAAM,EAAE,qBAAqB,EAMzD;IAEK,MAAM,CACV,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,OAAO,CAAC,CAgClB;CACF"}
@@ -1,5 +1,5 @@
1
1
  /* Copyright (C) 2026-present Aristotelis — see repository license. */
2
- import { PIPELINE_BEHAVIOR_ID, } from '@cqrs-ddd/pipeline';
2
+ import { PIPELINE_BEHAVIOR_CONTRACT, PIPELINE_BEHAVIOR_ID, } from '@cqrs-ddd/pipeline';
3
3
  import { CASL_ABILITY_KEY, CASL_PRINCIPAL_KEY } from './constants/tokens.js';
4
4
  import { UnauthorizedActionException } from './errors/unauthorized-action.exception.js';
5
5
  import { buildAbility } from './helpers/ability.js';
@@ -13,11 +13,33 @@ export const CASL_BEHAVIOR_ID = '@cqrs-ddd/pipeline-casl:CaslBehavior';
13
13
  * security-scoped keys, and throws `UnauthorizedActionException` unless every
14
14
  * requirement passes. An unauthenticated caller (`null` input) is denied.
15
15
  * Errors from the source or from malformed rules propagate unchanged.
16
- * A handler without `rules` runs without loading permissions.
16
+ * Placed globally, it passes a handler that declares no `rules` without loading
17
+ * permissions; a handler that places it itself must declare them, which the
18
+ * pipeline diagnostics check.
17
19
  */
18
20
  export class CaslBehavior {
19
21
  source;
20
22
  static [PIPELINE_BEHAVIOR_ID] = CASL_BEHAVIOR_ID;
23
+ /**
24
+ * A call-site declaration must name what it requires: without rules the
25
+ * handler would run unchecked. A global declaration may stay without rules.
26
+ */
27
+ static [PIPELINE_BEHAVIOR_CONTRACT] = {
28
+ validate: ({ declarationSource, effectiveOptions, handlerName }) => {
29
+ const rules = effectiveOptions?.rules;
30
+ const hasRules = Array.isArray(rules) && rules.length > 0;
31
+ if (declarationSource === 'global' || hasRules)
32
+ return undefined;
33
+ return [
34
+ {
35
+ handlerName,
36
+ behaviorName: CaslBehavior.name,
37
+ message: 'CaslBehavior is declared without rules, so the handler would run with no authorization check',
38
+ fix: 'Declare it with requires({ action, subject }), or remove it from this handler.',
39
+ },
40
+ ];
41
+ },
42
+ };
21
43
  constructor(source) {
22
44
  this.source = source;
23
45
  if (typeof source?.load !== 'function') {
@@ -1 +1 @@
1
- {"version":3,"file":"casl.behavior.js","sourceRoot":"","sources":["../src/casl.behavior.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAEtE,OAAO,EAIL,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAC;AACxF,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAIpD,iFAAiF;AACjF,MAAM,CAAC,MAAM,gBAAgB,GAAG,sCAAsC,CAAC;AAQvE;;;;;;;;;GASG;AACH,MAAM,OAAO,YAAY;IAGM,MAAM;IAFnC,MAAM,CAAU,CAAC,oBAAoB,CAAC,GAAG,gBAAgB,CAAC;IAE1D,YAA6B,MAA6B;sBAA7B,MAAM;QACjC,IAAI,OAAO,MAAM,EAAE,IAAI,KAAK,UAAU,EAAE,CAAC;YACvC,MAAM,IAAI,SAAS,CACjB,yFAAyF,CAC1F,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM,CACV,OAAyB,EACzB,IAAkB;QAElB,MAAM,KAAK,GACT,OAAO,CAAC,kBAAkB,CAAsB,YAAY,CAAC,EAAE,KAAK,CAAC;QACvE,IAAI,CAAC,KAAK,EAAE,MAAM;YAAE,OAAO,IAAI,EAAE,CAAC;QAElC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,2BAA2B,CAAC;gBACpC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM;gBACvB,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO;gBACzB,MAAM,EAAE,0CAA0C;aACnD,CAAC,CAAC;QACL,CAAC;QAED,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QAC3D,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,kBAAkB,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QACvD,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QAE7C,KAAK,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,KAAK,EAAE,CAAC;YAC/C,MAAM,OAAO,GAAG,KAAK;gBACnB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC;gBACrC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACjC,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,IAAI,2BAA2B,CAAC;oBACpC,MAAM;oBACN,OAAO;oBACP,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACtC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC;CACF"}
1
+ {"version":3,"file":"casl.behavior.js","sourceRoot":"","sources":["../src/casl.behavior.ts"],"names":[],"mappings":"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"}
package/dist/http.d.ts CHANGED
@@ -1,10 +1,6 @@
1
+ import type { HttpResponse } from '@cqrs-ddd/pipeline';
1
2
  import type { UnauthorizedActionException } from './errors/unauthorized-action.exception.js';
2
- /** An HTTP answer for an error: status, JSON body and headers. */
3
- export interface HttpResponse {
4
- status: number;
5
- body: Record<string, unknown>;
6
- headers: Record<string, string>;
7
- }
3
+ export type { HttpResponse };
8
4
  /**
9
5
  * The HTTP answer for an {@link UnauthorizedActionException}: `403 Forbidden`, naming the
10
6
  * denied action and subject.
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAC;AAE7F,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,2BAA2B,GACjC,YAAY,CAYd"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAC;AAE7F,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,2BAA2B,GACjC,YAAY,CAYd"}
package/dist/http.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAWtE;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAkC;IAElC,OAAO;QACL,MAAM,EAAE,GAAG;QACX,IAAI,EAAE;YACJ,UAAU,EAAE,GAAG;YACf,KAAK,EAAE,WAAW;YAClB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB;QACD,OAAO,EAAE,EAAE;KACZ,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,sEAAsE;AAOtE;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAkC;IAElC,OAAO;QACL,MAAM,EAAE,GAAG;QACX,IAAI,EAAE;YACJ,UAAU,EAAE,GAAG;YACf,KAAK,EAAE,WAAW;YAClB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB;QACD,OAAO,EAAE,EAAE;KACZ,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cqrs-ddd/pipeline-casl",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "CASL authorization for @cqrs-ddd/pipeline: type-level checks before the handler, entity and field checks after it",
5
5
  "author": "Aristotelis",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -39,17 +39,17 @@
39
39
  "access": "public"
40
40
  },
41
41
  "dependencies": {
42
- "@cqrs-ddd/safe-stringify": "^0.5.1"
42
+ "@cqrs-ddd/safe-stringify": "^0.6.0"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "@casl/ability": "^7.0.0",
46
- "@cqrs-ddd/pipeline": "^0.5.1"
46
+ "@cqrs-ddd/pipeline": "^0.6.0"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@casl/ability": "^7.0.2",
50
+ "@cqrs-ddd/pipeline": "0.6.0",
50
51
  "@vitest/coverage-v8": "^5.0.5",
51
- "vitest": "^5.0.5",
52
- "@cqrs-ddd/pipeline": "0.5.1"
52
+ "vitest": "^5.0.5"
53
53
  },
54
54
  "keywords": [
55
55
  "pipeline",