@o3co/auth.policy-verifier.builtins 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +70 -14
- package/README.md +75 -15
- package/dist/collectors/PayloadScopeCollector.d.mts +5 -0
- package/dist/collectors/PayloadScopeCollector.d.mts.map +1 -1
- package/dist/collectors/PayloadScopeCollector.mjs +8 -1
- package/dist/collectors/PayloadSubjectIdCollector.d.mts +9 -0
- package/dist/collectors/PayloadSubjectIdCollector.d.mts.map +1 -1
- package/dist/collectors/PayloadSubjectIdCollector.mjs +16 -4
- package/dist/collectors/RequestContextAttributeCollector.d.mts +47 -0
- package/dist/collectors/RequestContextAttributeCollector.d.mts.map +1 -0
- package/dist/collectors/RequestContextAttributeCollector.mjs +100 -0
- package/dist/collectors/StaticPermissionCollector.d.mts +5 -0
- package/dist/collectors/StaticPermissionCollector.d.mts.map +1 -1
- package/dist/collectors/StaticPermissionCollector.mjs +7 -0
- package/dist/collectors/StaticRoleCollector.d.mts +5 -0
- package/dist/collectors/StaticRoleCollector.d.mts.map +1 -1
- package/dist/collectors/StaticRoleCollector.mjs +7 -0
- package/dist/index.d.mts +3 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +4 -1
- package/dist/module.d.mts +5 -0
- package/dist/module.d.mts.map +1 -1
- package/dist/module.mjs +10 -1
- package/dist/resource/DotNotationResourceParser.d.mts +48 -0
- package/dist/resource/DotNotationResourceParser.d.mts.map +1 -1
- package/dist/resource/DotNotationResourceParser.mjs +94 -10
- package/dist/rules/AttrLiteralCompare.d.mts +2 -2
- package/dist/rules/AttrLiteralCompare.d.mts.map +1 -1
- package/dist/rules/AttrLiteralCompare.mjs +2 -0
- package/dist/rules/AttrLiteralEqual.d.mts +2 -2
- package/dist/rules/AttrLiteralEqual.d.mts.map +1 -1
- package/dist/rules/AttrLiteralEqual.mjs +2 -0
- package/dist/rules/AttrLiteralIn.d.mts +2 -2
- package/dist/rules/AttrLiteralIn.d.mts.map +1 -1
- package/dist/rules/AttrLiteralIn.mjs +2 -0
- package/dist/rules/AttrLiteralNotEqual.d.mts +2 -2
- package/dist/rules/AttrLiteralNotEqual.d.mts.map +1 -1
- package/dist/rules/AttrLiteralNotEqual.mjs +2 -0
- package/dist/rules/AttrLiteralNotIn.d.mts +2 -2
- package/dist/rules/AttrLiteralNotIn.d.mts.map +1 -1
- package/dist/rules/AttrLiteralNotIn.mjs +2 -0
- package/dist/rules/AttrMatchRule.d.mts.map +1 -1
- package/dist/rules/AttrMatchRule.mjs +2 -0
- package/dist/rules/AttrPairCompare.d.mts +2 -2
- package/dist/rules/AttrPairCompare.d.mts.map +1 -1
- package/dist/rules/AttrPairCompare.mjs +2 -0
- package/dist/rules/AttrPairEqual.d.mts +2 -2
- package/dist/rules/AttrPairEqual.d.mts.map +1 -1
- package/dist/rules/AttrPairEqual.mjs +2 -0
- package/dist/rules/AttrPairNotEqual.d.mts +2 -2
- package/dist/rules/AttrPairNotEqual.d.mts.map +1 -1
- package/dist/rules/AttrPairNotEqual.mjs +2 -0
- package/dist/rules/HasPermission.d.mts +36 -2
- package/dist/rules/HasPermission.d.mts.map +1 -1
- package/dist/rules/HasPermission.mjs +57 -6
- package/dist/rules/HasScope.d.mts +45 -4
- package/dist/rules/HasScope.d.mts.map +1 -1
- package/dist/rules/HasScope.mjs +46 -9
- package/dist/rules/_sharedValidation.d.mts +12 -9
- package/dist/rules/_sharedValidation.d.mts.map +1 -1
- package/dist/rules/_sharedValidation.mjs +46 -9
- package/dist/rules/collectors/ResourceActionPermissionRuleCollector.d.mts +6 -0
- package/dist/rules/collectors/ResourceActionPermissionRuleCollector.d.mts.map +1 -1
- package/dist/rules/collectors/ResourceActionPermissionRuleCollector.mjs +8 -0
- package/dist/rules/collectors/ResourceActionScopeRuleCollector.d.mts +52 -4
- package/dist/rules/collectors/ResourceActionScopeRuleCollector.d.mts.map +1 -1
- package/dist/rules/collectors/ResourceActionScopeRuleCollector.mjs +54 -6
- package/package.json +8 -5
- package/dist/__tests__/collectors/PayloadScopeCollector.test.d.mts +0 -2
- package/dist/__tests__/collectors/PayloadScopeCollector.test.d.mts.map +0 -1
- package/dist/__tests__/collectors/PayloadScopeCollector.test.mjs +0 -24
- package/dist/__tests__/collectors/PayloadSubjectIdCollector.test.d.mts +0 -2
- package/dist/__tests__/collectors/PayloadSubjectIdCollector.test.d.mts.map +0 -1
- package/dist/__tests__/collectors/PayloadSubjectIdCollector.test.mjs +0 -33
- package/dist/__tests__/collectors/StaticPermissionCollector.test.d.mts +0 -2
- package/dist/__tests__/collectors/StaticPermissionCollector.test.d.mts.map +0 -1
- package/dist/__tests__/collectors/StaticPermissionCollector.test.mjs +0 -22
- package/dist/__tests__/collectors/StaticRoleCollector.test.d.mts +0 -2
- package/dist/__tests__/collectors/StaticRoleCollector.test.d.mts.map +0 -1
- package/dist/__tests__/collectors/StaticRoleCollector.test.mjs +0 -24
- package/dist/__tests__/module.test.d.mts +0 -2
- package/dist/__tests__/module.test.d.mts.map +0 -1
- package/dist/__tests__/module.test.mjs +0 -71
- package/dist/__tests__/resource/DotNotationResourceParser.test.d.mts +0 -2
- package/dist/__tests__/resource/DotNotationResourceParser.test.d.mts.map +0 -1
- package/dist/__tests__/resource/DotNotationResourceParser.test.mjs +0 -34
- package/dist/__tests__/rules/AttrLiteralCompare.test.d.mts +0 -2
- package/dist/__tests__/rules/AttrLiteralCompare.test.d.mts.map +0 -1
- package/dist/__tests__/rules/AttrLiteralCompare.test.mjs +0 -253
- package/dist/__tests__/rules/AttrLiteralEqual.test.d.mts +0 -2
- package/dist/__tests__/rules/AttrLiteralEqual.test.d.mts.map +0 -1
- package/dist/__tests__/rules/AttrLiteralEqual.test.mjs +0 -138
- package/dist/__tests__/rules/AttrLiteralIn.test.d.mts +0 -2
- package/dist/__tests__/rules/AttrLiteralIn.test.d.mts.map +0 -1
- package/dist/__tests__/rules/AttrLiteralIn.test.mjs +0 -160
- package/dist/__tests__/rules/AttrLiteralNotEqual.test.d.mts +0 -2
- package/dist/__tests__/rules/AttrLiteralNotEqual.test.d.mts.map +0 -1
- package/dist/__tests__/rules/AttrLiteralNotEqual.test.mjs +0 -157
- package/dist/__tests__/rules/AttrLiteralNotIn.test.d.mts +0 -2
- package/dist/__tests__/rules/AttrLiteralNotIn.test.d.mts.map +0 -1
- package/dist/__tests__/rules/AttrLiteralNotIn.test.mjs +0 -168
- package/dist/__tests__/rules/AttrMatchRule.test.d.mts +0 -2
- package/dist/__tests__/rules/AttrMatchRule.test.d.mts.map +0 -1
- package/dist/__tests__/rules/AttrMatchRule.test.mjs +0 -79
- package/dist/__tests__/rules/AttrPairCompare.test.d.mts +0 -2
- package/dist/__tests__/rules/AttrPairCompare.test.d.mts.map +0 -1
- package/dist/__tests__/rules/AttrPairCompare.test.mjs +0 -319
- package/dist/__tests__/rules/AttrPairEqual.test.d.mts +0 -2
- package/dist/__tests__/rules/AttrPairEqual.test.d.mts.map +0 -1
- package/dist/__tests__/rules/AttrPairEqual.test.mjs +0 -155
- package/dist/__tests__/rules/AttrPairNotEqual.test.d.mts +0 -2
- package/dist/__tests__/rules/AttrPairNotEqual.test.d.mts.map +0 -1
- package/dist/__tests__/rules/AttrPairNotEqual.test.mjs +0 -155
- package/dist/__tests__/rules/HasPermission.test.d.mts +0 -2
- package/dist/__tests__/rules/HasPermission.test.d.mts.map +0 -1
- package/dist/__tests__/rules/HasPermission.test.mjs +0 -86
- package/dist/__tests__/rules/HasScope.test.d.mts +0 -2
- package/dist/__tests__/rules/HasScope.test.d.mts.map +0 -1
- package/dist/__tests__/rules/HasScope.test.mjs +0 -42
- package/dist/__tests__/rules/_sharedValidation.test.d.mts +0 -2
- package/dist/__tests__/rules/_sharedValidation.test.d.mts.map +0 -1
- package/dist/__tests__/rules/_sharedValidation.test.mjs +0 -325
- package/dist/__tests__/rules/collectors/ResourceActionPermissionRuleCollector.test.d.mts +0 -2
- package/dist/__tests__/rules/collectors/ResourceActionPermissionRuleCollector.test.d.mts.map +0 -1
- package/dist/__tests__/rules/collectors/ResourceActionPermissionRuleCollector.test.mjs +0 -27
- package/dist/__tests__/rules/collectors/ResourceActionScopeRuleCollector.test.d.mts +0 -2
- package/dist/__tests__/rules/collectors/ResourceActionScopeRuleCollector.test.d.mts.map +0 -1
- package/dist/__tests__/rules/collectors/ResourceActionScopeRuleCollector.test.mjs +0 -48
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { Registry, } from "@o3co/auth.policy-verifier.core";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { builtinCollectorsModule } from "../module.mjs";
|
|
4
|
-
describe("builtinCollectorsModule", () => {
|
|
5
|
-
it("has name 'builtin-collectors'", () => {
|
|
6
|
-
expect(builtinCollectorsModule.name).toBe("builtin-collectors");
|
|
7
|
-
});
|
|
8
|
-
it("registers all builtin attribute collector factories", async () => {
|
|
9
|
-
const attributeCollectorRegistry = new Registry();
|
|
10
|
-
const ruleCollectorRegistry = new Registry();
|
|
11
|
-
const resourceParserRegistry = new Registry();
|
|
12
|
-
await builtinCollectorsModule.init({
|
|
13
|
-
pathResolver: (s) => s,
|
|
14
|
-
config: {},
|
|
15
|
-
attributeCollectorRegistry,
|
|
16
|
-
ruleCollectorRegistry,
|
|
17
|
-
resourceParserRegistry,
|
|
18
|
-
});
|
|
19
|
-
expect(attributeCollectorRegistry.has("PayloadScopeCollector")).toBe(true);
|
|
20
|
-
expect(attributeCollectorRegistry.has("PayloadSubjectIdCollector")).toBe(true);
|
|
21
|
-
expect(attributeCollectorRegistry.has("StaticPermissionCollector")).toBe(true);
|
|
22
|
-
expect(attributeCollectorRegistry.has("StaticRoleCollector")).toBe(true);
|
|
23
|
-
});
|
|
24
|
-
it("registers all builtin rule collector factories", async () => {
|
|
25
|
-
const attributeCollectorRegistry = new Registry();
|
|
26
|
-
const ruleCollectorRegistry = new Registry();
|
|
27
|
-
const resourceParserRegistry = new Registry();
|
|
28
|
-
await builtinCollectorsModule.init({
|
|
29
|
-
pathResolver: (s) => s,
|
|
30
|
-
config: {},
|
|
31
|
-
attributeCollectorRegistry,
|
|
32
|
-
ruleCollectorRegistry,
|
|
33
|
-
resourceParserRegistry,
|
|
34
|
-
});
|
|
35
|
-
expect(ruleCollectorRegistry.has("ResourceActionScopeRuleCollector")).toBe(true);
|
|
36
|
-
expect(ruleCollectorRegistry.has("ResourceActionPermissionRuleCollector")).toBe(true);
|
|
37
|
-
});
|
|
38
|
-
it("registers DotNotationResourceParser factory", async () => {
|
|
39
|
-
const attributeCollectorRegistry = new Registry();
|
|
40
|
-
const ruleCollectorRegistry = new Registry();
|
|
41
|
-
const resourceParserRegistry = new Registry();
|
|
42
|
-
await builtinCollectorsModule.init({
|
|
43
|
-
pathResolver: (s) => s,
|
|
44
|
-
config: {},
|
|
45
|
-
attributeCollectorRegistry,
|
|
46
|
-
ruleCollectorRegistry,
|
|
47
|
-
resourceParserRegistry,
|
|
48
|
-
});
|
|
49
|
-
expect(resourceParserRegistry.has("DotNotationResourceParser")).toBe(true);
|
|
50
|
-
});
|
|
51
|
-
it("creates a working StaticPermissionCollector from factory with config", async () => {
|
|
52
|
-
const attributeCollectorRegistry = new Registry();
|
|
53
|
-
const ruleCollectorRegistry = new Registry();
|
|
54
|
-
const resourceParserRegistry = new Registry();
|
|
55
|
-
await builtinCollectorsModule.init({
|
|
56
|
-
pathResolver: (s) => s,
|
|
57
|
-
config: {},
|
|
58
|
-
attributeCollectorRegistry,
|
|
59
|
-
ruleCollectorRegistry,
|
|
60
|
-
resourceParserRegistry,
|
|
61
|
-
});
|
|
62
|
-
const factory = attributeCollectorRegistry.get("StaticPermissionCollector");
|
|
63
|
-
const collector = factory({ permissions: ["admin", "read"] });
|
|
64
|
-
const attrs = await collector.collect({
|
|
65
|
-
payload: {},
|
|
66
|
-
resource: { raw: "test", resourceType: "test" },
|
|
67
|
-
action: "read",
|
|
68
|
-
});
|
|
69
|
-
expect(attrs.get("permissions")).toEqual(["admin", "read"]);
|
|
70
|
-
});
|
|
71
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DotNotationResourceParser.test.d.mts","sourceRoot":"","sources":["../../../src/__tests__/resource/DotNotationResourceParser.test.mts"],"names":[],"mappings":""}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { DotNotationResourceParser } from "#/resource/DotNotationResourceParser.mjs";
|
|
3
|
-
describe("DotNotationResourceParser", () => {
|
|
4
|
-
const parser = new DotNotationResourceParser();
|
|
5
|
-
it("parses simple type:id", () => {
|
|
6
|
-
const r = parser.parse("project:1");
|
|
7
|
-
expect(r.raw).toBe("project:1");
|
|
8
|
-
expect(r.resourceType).toBe("project");
|
|
9
|
-
expect(r.resourceId).toBe("1");
|
|
10
|
-
});
|
|
11
|
-
it("parses type-only (no id)", () => {
|
|
12
|
-
const r = parser.parse("user");
|
|
13
|
-
expect(r.raw).toBe("user");
|
|
14
|
-
expect(r.resourceType).toBe("user");
|
|
15
|
-
expect(r.resourceId).toBeUndefined();
|
|
16
|
-
});
|
|
17
|
-
it("parses nested type:id.type:id", () => {
|
|
18
|
-
const r = parser.parse("project:1.member:2");
|
|
19
|
-
expect(r.raw).toBe("project:1.member:2");
|
|
20
|
-
expect(r.resourceType).toBe("project_member");
|
|
21
|
-
expect(r.resourceId).toBe("2");
|
|
22
|
-
});
|
|
23
|
-
it("parses nested type:id.type (last has no id)", () => {
|
|
24
|
-
const r = parser.parse("project:1.member");
|
|
25
|
-
expect(r.raw).toBe("project:1.member");
|
|
26
|
-
expect(r.resourceType).toBe("project_member");
|
|
27
|
-
expect(r.resourceId).toBeUndefined();
|
|
28
|
-
});
|
|
29
|
-
it("trims whitespace", () => {
|
|
30
|
-
const r = parser.parse(" project : 1 . member : 2 ");
|
|
31
|
-
expect(r.resourceType).toBe("project_member");
|
|
32
|
-
expect(r.resourceId).toBe("2");
|
|
33
|
-
});
|
|
34
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AttrLiteralCompare.test.d.mts","sourceRoot":"","sources":["../../../src/__tests__/rules/AttrLiteralCompare.test.mts"],"names":[],"mappings":""}
|
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { AttrLiteralCompare } from "#/rules/AttrLiteralCompare.mjs";
|
|
3
|
-
describe("AttrLiteralCompare", () => {
|
|
4
|
-
// ---------------------------------------------------------------------------
|
|
5
|
-
// Happy path: each op at boundary values
|
|
6
|
-
// ---------------------------------------------------------------------------
|
|
7
|
-
it("op=lt: returns true when attr < v (5 < 10)", () => {
|
|
8
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "lt", v: 10 });
|
|
9
|
-
const attrs = new Map([["score", 5]]);
|
|
10
|
-
expect(rule.verify(attrs)).toBe(true);
|
|
11
|
-
});
|
|
12
|
-
it("op=lt: returns false when attr === v (10 < 10 is false)", () => {
|
|
13
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "lt", v: 10 });
|
|
14
|
-
const attrs = new Map([["score", 10]]);
|
|
15
|
-
expect(rule.verify(attrs)).toBe(false);
|
|
16
|
-
});
|
|
17
|
-
it("op=lt: returns false when attr > v (15 < 10 is false)", () => {
|
|
18
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "lt", v: 10 });
|
|
19
|
-
const attrs = new Map([["score", 15]]);
|
|
20
|
-
expect(rule.verify(attrs)).toBe(false);
|
|
21
|
-
});
|
|
22
|
-
it("op=le: returns true when attr === v (10 <= 10)", () => {
|
|
23
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "le", v: 10 });
|
|
24
|
-
const attrs = new Map([["score", 10]]);
|
|
25
|
-
expect(rule.verify(attrs)).toBe(true);
|
|
26
|
-
});
|
|
27
|
-
it("op=le: returns true when attr < v (5 <= 10)", () => {
|
|
28
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "le", v: 10 });
|
|
29
|
-
const attrs = new Map([["score", 5]]);
|
|
30
|
-
expect(rule.verify(attrs)).toBe(true);
|
|
31
|
-
});
|
|
32
|
-
it("op=le: returns false when attr > v (15 <= 10 is false)", () => {
|
|
33
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "le", v: 10 });
|
|
34
|
-
const attrs = new Map([["score", 15]]);
|
|
35
|
-
expect(rule.verify(attrs)).toBe(false);
|
|
36
|
-
});
|
|
37
|
-
it("op=gt: returns true when attr > v (15 > 10)", () => {
|
|
38
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "gt", v: 10 });
|
|
39
|
-
const attrs = new Map([["score", 15]]);
|
|
40
|
-
expect(rule.verify(attrs)).toBe(true);
|
|
41
|
-
});
|
|
42
|
-
it("op=gt: returns false when attr === v (10 > 10 is false)", () => {
|
|
43
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "gt", v: 10 });
|
|
44
|
-
const attrs = new Map([["score", 10]]);
|
|
45
|
-
expect(rule.verify(attrs)).toBe(false);
|
|
46
|
-
});
|
|
47
|
-
it("op=gt: returns false when attr < v (5 > 10 is false)", () => {
|
|
48
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "gt", v: 10 });
|
|
49
|
-
const attrs = new Map([["score", 5]]);
|
|
50
|
-
expect(rule.verify(attrs)).toBe(false);
|
|
51
|
-
});
|
|
52
|
-
it("op=ge: returns true when attr === v (10 >= 10)", () => {
|
|
53
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "ge", v: 10 });
|
|
54
|
-
const attrs = new Map([["score", 10]]);
|
|
55
|
-
expect(rule.verify(attrs)).toBe(true);
|
|
56
|
-
});
|
|
57
|
-
it("op=ge: returns true when attr > v (15 >= 10)", () => {
|
|
58
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "ge", v: 10 });
|
|
59
|
-
const attrs = new Map([["score", 15]]);
|
|
60
|
-
expect(rule.verify(attrs)).toBe(true);
|
|
61
|
-
});
|
|
62
|
-
it("op=ge: returns false when attr < v (5 >= 10 is false)", () => {
|
|
63
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "ge", v: 10 });
|
|
64
|
-
const attrs = new Map([["score", 5]]);
|
|
65
|
-
expect(rule.verify(attrs)).toBe(false);
|
|
66
|
-
});
|
|
67
|
-
// ---------------------------------------------------------------------------
|
|
68
|
-
// Evaluation-time safe-deny: attr is not a number
|
|
69
|
-
// ---------------------------------------------------------------------------
|
|
70
|
-
it("returns false when attr is a string (not a number)", () => {
|
|
71
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "gt", v: 5 });
|
|
72
|
-
const attrs = new Map([["score", "10"]]);
|
|
73
|
-
expect(rule.verify(attrs)).toBe(false);
|
|
74
|
-
});
|
|
75
|
-
it("returns false when attr is a boolean (not a number)", () => {
|
|
76
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "gt", v: 0 });
|
|
77
|
-
const attrs = new Map([["score", true]]);
|
|
78
|
-
expect(rule.verify(attrs)).toBe(false);
|
|
79
|
-
});
|
|
80
|
-
// ---------------------------------------------------------------------------
|
|
81
|
-
// Evaluation-time: NaN attribute → false for all ops
|
|
82
|
-
// ---------------------------------------------------------------------------
|
|
83
|
-
it("op=lt: returns false when attr is NaN", () => {
|
|
84
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "lt", v: 10 });
|
|
85
|
-
const attrs = new Map([["score", NaN]]);
|
|
86
|
-
expect(rule.verify(attrs)).toBe(false);
|
|
87
|
-
});
|
|
88
|
-
it("op=le: returns false when attr is NaN", () => {
|
|
89
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "le", v: 10 });
|
|
90
|
-
const attrs = new Map([["score", NaN]]);
|
|
91
|
-
expect(rule.verify(attrs)).toBe(false);
|
|
92
|
-
});
|
|
93
|
-
it("op=gt: returns false when attr is NaN", () => {
|
|
94
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "gt", v: 10 });
|
|
95
|
-
const attrs = new Map([["score", NaN]]);
|
|
96
|
-
expect(rule.verify(attrs)).toBe(false);
|
|
97
|
-
});
|
|
98
|
-
it("op=ge: returns false when attr is NaN", () => {
|
|
99
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "ge", v: 10 });
|
|
100
|
-
const attrs = new Map([["score", NaN]]);
|
|
101
|
-
expect(rule.verify(attrs)).toBe(false);
|
|
102
|
-
});
|
|
103
|
-
// ---------------------------------------------------------------------------
|
|
104
|
-
// Evaluation-time: Infinity / -Infinity attributes
|
|
105
|
-
// ---------------------------------------------------------------------------
|
|
106
|
-
it("op=gt: Infinity > 100 returns true", () => {
|
|
107
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "gt", v: 100 });
|
|
108
|
-
const attrs = new Map([["score", Infinity]]);
|
|
109
|
-
expect(rule.verify(attrs)).toBe(true);
|
|
110
|
-
});
|
|
111
|
-
it("op=lt: -Infinity < 0 returns true", () => {
|
|
112
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "lt", v: 0 });
|
|
113
|
-
const attrs = new Map([["score", -Infinity]]);
|
|
114
|
-
expect(rule.verify(attrs)).toBe(true);
|
|
115
|
-
});
|
|
116
|
-
it("op=ge: Infinity >= Infinity returns true", () => {
|
|
117
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "ge", v: Infinity });
|
|
118
|
-
const attrs = new Map([["score", Infinity]]);
|
|
119
|
-
expect(rule.verify(attrs)).toBe(true);
|
|
120
|
-
});
|
|
121
|
-
// ---------------------------------------------------------------------------
|
|
122
|
-
// Evaluation-time safe-deny: missing / null attr
|
|
123
|
-
// ---------------------------------------------------------------------------
|
|
124
|
-
it("returns false when the target attribute is missing", () => {
|
|
125
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "gt", v: 0 });
|
|
126
|
-
const attrs = new Map();
|
|
127
|
-
expect(rule.verify(attrs)).toBe(false);
|
|
128
|
-
});
|
|
129
|
-
it("returns false when the target attribute is null", () => {
|
|
130
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "gt", v: 0 });
|
|
131
|
-
const attrs = new Map([["score", null]]);
|
|
132
|
-
expect(rule.verify(attrs)).toBe(false);
|
|
133
|
-
});
|
|
134
|
-
// ---------------------------------------------------------------------------
|
|
135
|
-
// Construction errors: invalid 'a'
|
|
136
|
-
// ---------------------------------------------------------------------------
|
|
137
|
-
it("throws when 'a' is undefined", () => {
|
|
138
|
-
expect(() => new AttrLiteralCompare({ a: undefined, op: "gt", v: 0 })).toThrow(/AttrLiteralCompare.*'a'.*(non-empty string|got undefined)/);
|
|
139
|
-
});
|
|
140
|
-
it("throws when 'a' is an empty string", () => {
|
|
141
|
-
expect(() => new AttrLiteralCompare({ a: "", op: "gt", v: 0 })).toThrow(/AttrLiteralCompare.*'a'.*empty string/);
|
|
142
|
-
});
|
|
143
|
-
it("throws when 'a' is a non-string (number 42)", () => {
|
|
144
|
-
expect(() => new AttrLiteralCompare({ a: 42, op: "gt", v: 0 })).toThrow(/AttrLiteralCompare.*'a'.*got number/);
|
|
145
|
-
});
|
|
146
|
-
// ---------------------------------------------------------------------------
|
|
147
|
-
// Construction errors: invalid 'v'
|
|
148
|
-
// ---------------------------------------------------------------------------
|
|
149
|
-
it("throws when 'v' is missing (undefined)", () => {
|
|
150
|
-
expect(() => new AttrLiteralCompare({ a: "score", op: "gt", v: undefined })).toThrow(/AttrLiteralCompare.*'v'/);
|
|
151
|
-
});
|
|
152
|
-
it("throws when 'v' is null", () => {
|
|
153
|
-
expect(() => new AttrLiteralCompare({ a: "score", op: "gt", v: null })).toThrow(/AttrLiteralCompare.*'v'/);
|
|
154
|
-
});
|
|
155
|
-
it("throws when 'v' is a string", () => {
|
|
156
|
-
expect(() => new AttrLiteralCompare({ a: "score", op: "gt", v: "10" })).toThrow(/AttrLiteralCompare.*'v'/);
|
|
157
|
-
});
|
|
158
|
-
it("throws when 'v' is a boolean", () => {
|
|
159
|
-
expect(() => new AttrLiteralCompare({ a: "score", op: "gt", v: true })).toThrow(/AttrLiteralCompare.*'v'/);
|
|
160
|
-
});
|
|
161
|
-
it("throws when 'v' is a plain object", () => {
|
|
162
|
-
expect(() => new AttrLiteralCompare({ a: "score", op: "gt", v: {} })).toThrow(/AttrLiteralCompare.*'v'/);
|
|
163
|
-
});
|
|
164
|
-
it("throws when 'v' is NaN (fail-fast at construction)", () => {
|
|
165
|
-
expect(() => new AttrLiteralCompare({ a: "score", op: "gt", v: NaN })).toThrow(/AttrLiteralCompare.*'v'.*NaN/);
|
|
166
|
-
});
|
|
167
|
-
// ---------------------------------------------------------------------------
|
|
168
|
-
// Construction errors: invalid 'op'
|
|
169
|
-
// ---------------------------------------------------------------------------
|
|
170
|
-
it("throws when 'op' is missing (undefined)", () => {
|
|
171
|
-
expect(() => new AttrLiteralCompare({
|
|
172
|
-
a: "score",
|
|
173
|
-
op: undefined,
|
|
174
|
-
v: 0,
|
|
175
|
-
})).toThrow(/AttrLiteralCompare.*'op'/);
|
|
176
|
-
});
|
|
177
|
-
it("throws when 'op' is not a string (number)", () => {
|
|
178
|
-
expect(() => new AttrLiteralCompare({
|
|
179
|
-
a: "score",
|
|
180
|
-
op: 42,
|
|
181
|
-
v: 0,
|
|
182
|
-
})).toThrow(/AttrLiteralCompare.*'op'/);
|
|
183
|
-
});
|
|
184
|
-
it("throws when 'op' is an invalid string ('eq')", () => {
|
|
185
|
-
expect(() => new AttrLiteralCompare({
|
|
186
|
-
a: "score",
|
|
187
|
-
op: "eq",
|
|
188
|
-
v: 0,
|
|
189
|
-
})).toThrow(/AttrLiteralCompare.*'op'/);
|
|
190
|
-
});
|
|
191
|
-
// ---------------------------------------------------------------------------
|
|
192
|
-
// Construction errors: invalid 'group'
|
|
193
|
-
// ---------------------------------------------------------------------------
|
|
194
|
-
it("throws when 'group' is present but is an empty string", () => {
|
|
195
|
-
expect(() => new AttrLiteralCompare({ a: "score", op: "gt", v: 0, group: "" })).toThrow(/AttrLiteralCompare.*'group'.*empty string/);
|
|
196
|
-
});
|
|
197
|
-
it("throws when 'group' is present but is a non-string (number 42)", () => {
|
|
198
|
-
expect(() => new AttrLiteralCompare({
|
|
199
|
-
a: "score",
|
|
200
|
-
op: "gt",
|
|
201
|
-
v: 0,
|
|
202
|
-
group: 42,
|
|
203
|
-
})).toThrow(/AttrLiteralCompare.*'group'.*got number/);
|
|
204
|
-
});
|
|
205
|
-
// ---------------------------------------------------------------------------
|
|
206
|
-
// ruleType: default derived from a, op, v
|
|
207
|
-
// ---------------------------------------------------------------------------
|
|
208
|
-
it("derives default ruleType from a, op, and v", () => {
|
|
209
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "gt", v: 10 });
|
|
210
|
-
expect(rule.ruleType).toBe("attr_literal_compare:score:gt:10");
|
|
211
|
-
});
|
|
212
|
-
it("derives default ruleType for negative v", () => {
|
|
213
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "lt", v: -5 });
|
|
214
|
-
expect(rule.ruleType).toBe("attr_literal_compare:score:lt:-5");
|
|
215
|
-
});
|
|
216
|
-
it("derives default ruleType for Infinity v", () => {
|
|
217
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "lt", v: Infinity });
|
|
218
|
-
expect(rule.ruleType).toBe("attr_literal_compare:score:lt:Infinity");
|
|
219
|
-
});
|
|
220
|
-
it("distinct op or v produce distinct ruleTypes (AND semantics)", () => {
|
|
221
|
-
const r1 = new AttrLiteralCompare({ a: "score", op: "gt", v: 10 });
|
|
222
|
-
const r2 = new AttrLiteralCompare({ a: "score", op: "ge", v: 10 });
|
|
223
|
-
const r3 = new AttrLiteralCompare({ a: "score", op: "gt", v: 20 });
|
|
224
|
-
expect(r1.ruleType).not.toBe(r2.ruleType);
|
|
225
|
-
expect(r1.ruleType).not.toBe(r3.ruleType);
|
|
226
|
-
});
|
|
227
|
-
// ---------------------------------------------------------------------------
|
|
228
|
-
// ruleType: group override
|
|
229
|
-
// ---------------------------------------------------------------------------
|
|
230
|
-
it("uses the provided group string as ruleType, overriding the default", () => {
|
|
231
|
-
const r1 = new AttrLiteralCompare({ a: "score", op: "gt", v: 10, group: "score-check" });
|
|
232
|
-
const r2 = new AttrLiteralCompare({ a: "score", op: "ge", v: 11, group: "score-check" });
|
|
233
|
-
expect(r1.ruleType).toBe("score-check");
|
|
234
|
-
expect(r2.ruleType).toBe("score-check");
|
|
235
|
-
});
|
|
236
|
-
// ---------------------------------------------------------------------------
|
|
237
|
-
// code and message fields
|
|
238
|
-
// ---------------------------------------------------------------------------
|
|
239
|
-
it("has code 'attr_compare_violated'", () => {
|
|
240
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "gt", v: 10 });
|
|
241
|
-
expect(rule.code).toBe("attr_compare_violated");
|
|
242
|
-
});
|
|
243
|
-
it("message contains the attribute name, op, and v", () => {
|
|
244
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "gt", v: 10 });
|
|
245
|
-
expect(rule.message).toContain("score");
|
|
246
|
-
expect(rule.message).toContain("gt");
|
|
247
|
-
expect(rule.message).toContain("10");
|
|
248
|
-
});
|
|
249
|
-
it("message starts with a capital letter", () => {
|
|
250
|
-
const rule = new AttrLiteralCompare({ a: "score", op: "gt", v: 10 });
|
|
251
|
-
expect(rule.message[0]).toBe(rule.message[0]?.toUpperCase());
|
|
252
|
-
});
|
|
253
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AttrLiteralEqual.test.d.mts","sourceRoot":"","sources":["../../../src/__tests__/rules/AttrLiteralEqual.test.mts"],"names":[],"mappings":""}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { AttrLiteralEqual } from "#/rules/AttrLiteralEqual.mjs";
|
|
3
|
-
describe("AttrLiteralEqual", () => {
|
|
4
|
-
// ---------------------------------------------------------------------------
|
|
5
|
-
// Happy path: verify() returns true
|
|
6
|
-
// ---------------------------------------------------------------------------
|
|
7
|
-
it("returns true when attr equals the configured string literal", () => {
|
|
8
|
-
const rule = new AttrLiteralEqual({ a: "role", v: "admin" });
|
|
9
|
-
const attrs = new Map([["role", "admin"]]);
|
|
10
|
-
expect(rule.verify(attrs)).toBe(true);
|
|
11
|
-
});
|
|
12
|
-
it("returns true when attr equals the configured number literal", () => {
|
|
13
|
-
const rule = new AttrLiteralEqual({ a: "age", v: 30 });
|
|
14
|
-
const attrs = new Map([["age", 30]]);
|
|
15
|
-
expect(rule.verify(attrs)).toBe(true);
|
|
16
|
-
});
|
|
17
|
-
it("returns true when attr equals the configured boolean literal", () => {
|
|
18
|
-
const rule = new AttrLiteralEqual({ a: "verified", v: true });
|
|
19
|
-
const attrs = new Map([["verified", true]]);
|
|
20
|
-
expect(rule.verify(attrs)).toBe(true);
|
|
21
|
-
});
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
// Type mismatch: no coercion
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
it("returns false when number literal v=30 but attr is the string '30' (no coercion)", () => {
|
|
26
|
-
const rule = new AttrLiteralEqual({ a: "age", v: 30 });
|
|
27
|
-
const attrs = new Map([["age", "30"]]);
|
|
28
|
-
expect(rule.verify(attrs)).toBe(false);
|
|
29
|
-
});
|
|
30
|
-
it("returns false when string literal v='true' but attr is the boolean true (no coercion)", () => {
|
|
31
|
-
const rule = new AttrLiteralEqual({ a: "verified", v: "true" });
|
|
32
|
-
const attrs = new Map([["verified", true]]);
|
|
33
|
-
expect(rule.verify(attrs)).toBe(false);
|
|
34
|
-
});
|
|
35
|
-
// ---------------------------------------------------------------------------
|
|
36
|
-
// Evaluation-time safe-deny: missing / null attr
|
|
37
|
-
// ---------------------------------------------------------------------------
|
|
38
|
-
it("returns false when the target attribute is missing", () => {
|
|
39
|
-
const rule = new AttrLiteralEqual({ a: "role", v: "admin" });
|
|
40
|
-
const attrs = new Map();
|
|
41
|
-
expect(rule.verify(attrs)).toBe(false);
|
|
42
|
-
});
|
|
43
|
-
it("returns false when the target attribute is null", () => {
|
|
44
|
-
const rule = new AttrLiteralEqual({ a: "role", v: "admin" });
|
|
45
|
-
const attrs = new Map([["role", null]]);
|
|
46
|
-
expect(rule.verify(attrs)).toBe(false);
|
|
47
|
-
});
|
|
48
|
-
// ---------------------------------------------------------------------------
|
|
49
|
-
// Construction errors: invalid 'a'
|
|
50
|
-
// ---------------------------------------------------------------------------
|
|
51
|
-
it("throws when 'a' is undefined", () => {
|
|
52
|
-
expect(() => new AttrLiteralEqual({ a: undefined, v: "x" })).toThrow(/AttrLiteralEqual.*'a'.*(non-empty string|got undefined)/);
|
|
53
|
-
});
|
|
54
|
-
it("throws when 'a' is an empty string", () => {
|
|
55
|
-
expect(() => new AttrLiteralEqual({ a: "", v: "x" })).toThrow(/AttrLiteralEqual.*'a'.*empty string/);
|
|
56
|
-
});
|
|
57
|
-
it("throws when 'a' is a non-string (number 42)", () => {
|
|
58
|
-
expect(() => new AttrLiteralEqual({ a: 42, v: "x" })).toThrow(/AttrLiteralEqual.*'a'.*got number/);
|
|
59
|
-
});
|
|
60
|
-
// ---------------------------------------------------------------------------
|
|
61
|
-
// Construction errors: invalid 'v'
|
|
62
|
-
// ---------------------------------------------------------------------------
|
|
63
|
-
it("throws when 'v' is null", () => {
|
|
64
|
-
expect(() => new AttrLiteralEqual({ a: "role", v: null })).toThrow(/AttrLiteralEqual.*'v'.*got null/);
|
|
65
|
-
});
|
|
66
|
-
it("throws when 'v' is undefined", () => {
|
|
67
|
-
expect(() => new AttrLiteralEqual({ a: "role", v: undefined })).toThrow(/AttrLiteralEqual.*'v'.*got undefined/);
|
|
68
|
-
});
|
|
69
|
-
it("throws when 'v' is a plain object ({})", () => {
|
|
70
|
-
expect(() => new AttrLiteralEqual({ a: "role", v: {} })).toThrow(/AttrLiteralEqual.*'v'.*got object/);
|
|
71
|
-
});
|
|
72
|
-
it("throws when 'v' is NaN", () => {
|
|
73
|
-
// NaN === NaN is always false, so AttrLiteralEqual({ v: NaN }) would be
|
|
74
|
-
// an always-deny rule. Must fail-fast at construction rather than ship
|
|
75
|
-
// a silently-broken predicate.
|
|
76
|
-
expect(() => new AttrLiteralEqual({ a: "score", v: Number.NaN })).toThrow(/must not be NaN/);
|
|
77
|
-
});
|
|
78
|
-
// ---------------------------------------------------------------------------
|
|
79
|
-
// Construction errors: invalid 'group'
|
|
80
|
-
// ---------------------------------------------------------------------------
|
|
81
|
-
it("throws when 'group' is present but is an empty string", () => {
|
|
82
|
-
expect(() => new AttrLiteralEqual({ a: "role", v: "admin", group: "" })).toThrow(/AttrLiteralEqual.*'group'.*empty string/);
|
|
83
|
-
});
|
|
84
|
-
it("throws when 'group' is present but is a non-string (number 42)", () => {
|
|
85
|
-
expect(() => new AttrLiteralEqual({ a: "role", v: "admin", group: 42 })).toThrow(/AttrLiteralEqual.*'group'.*got number/);
|
|
86
|
-
});
|
|
87
|
-
// ---------------------------------------------------------------------------
|
|
88
|
-
// ruleType: default derived from 'a', 'typeof v', and 'String(v)'
|
|
89
|
-
// ---------------------------------------------------------------------------
|
|
90
|
-
it("derives default ruleType including the type segment for a string literal", () => {
|
|
91
|
-
const rule = new AttrLiteralEqual({ a: "role", v: "admin" });
|
|
92
|
-
expect(rule.ruleType).toBe("attr_literal_equal:role:string:admin");
|
|
93
|
-
});
|
|
94
|
-
it("derives default ruleType including the type segment for a number literal", () => {
|
|
95
|
-
const rule = new AttrLiteralEqual({ a: "age", v: 30 });
|
|
96
|
-
expect(rule.ruleType).toBe("attr_literal_equal:age:number:30");
|
|
97
|
-
});
|
|
98
|
-
it("derives default ruleType including the type segment for a boolean literal", () => {
|
|
99
|
-
const rule = new AttrLiteralEqual({ a: "verified", v: true });
|
|
100
|
-
expect(rule.ruleType).toBe("attr_literal_equal:verified:boolean:true");
|
|
101
|
-
});
|
|
102
|
-
it("distinct configs produce distinct default ruleTypes (AND semantics)", () => {
|
|
103
|
-
const r1 = new AttrLiteralEqual({ a: "role", v: "admin" });
|
|
104
|
-
const r2 = new AttrLiteralEqual({ a: "role", v: "editor" });
|
|
105
|
-
expect(r1.ruleType).not.toBe(r2.ruleType);
|
|
106
|
-
});
|
|
107
|
-
it("distinguishes literals by type in ruleType (e.g. boolean true vs string 'true')", () => {
|
|
108
|
-
// Regression guard: without the typeof-segment in ruleType, these two
|
|
109
|
-
// rules would share the same default ruleType and the evaluator would
|
|
110
|
-
// silently OR them together, weakening authorization. The type tag
|
|
111
|
-
// keeps them AND-combined as intended.
|
|
112
|
-
const r1 = new AttrLiteralEqual({ a: "status", v: true });
|
|
113
|
-
const r2 = new AttrLiteralEqual({ a: "status", v: "true" });
|
|
114
|
-
expect(r1.ruleType).not.toBe(r2.ruleType);
|
|
115
|
-
});
|
|
116
|
-
// ---------------------------------------------------------------------------
|
|
117
|
-
// ruleType: group override
|
|
118
|
-
// ---------------------------------------------------------------------------
|
|
119
|
-
it("uses the provided group string as ruleType, overriding the default", () => {
|
|
120
|
-
const r1 = new AttrLiteralEqual({ a: "role", v: "admin", group: "any-admin-role" });
|
|
121
|
-
const r2 = new AttrLiteralEqual({ a: "role", v: "superuser", group: "any-admin-role" });
|
|
122
|
-
expect(r1.ruleType).toBe("any-admin-role");
|
|
123
|
-
expect(r2.ruleType).toBe("any-admin-role");
|
|
124
|
-
});
|
|
125
|
-
// ---------------------------------------------------------------------------
|
|
126
|
-
// code and message fields
|
|
127
|
-
// ---------------------------------------------------------------------------
|
|
128
|
-
it("has code 'attr_not_equal'", () => {
|
|
129
|
-
const rule = new AttrLiteralEqual({ a: "role", v: "admin" });
|
|
130
|
-
expect(rule.code).toBe("attr_not_equal");
|
|
131
|
-
});
|
|
132
|
-
it("message contains the attribute name and literal value, and starts with a capital letter", () => {
|
|
133
|
-
const rule = new AttrLiteralEqual({ a: "role", v: "admin" });
|
|
134
|
-
expect(rule.message).toContain("role");
|
|
135
|
-
expect(rule.message).toContain("admin");
|
|
136
|
-
expect(rule.message[0]).toBe(rule.message[0]?.toUpperCase());
|
|
137
|
-
});
|
|
138
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AttrLiteralIn.test.d.mts","sourceRoot":"","sources":["../../../src/__tests__/rules/AttrLiteralIn.test.mts"],"names":[],"mappings":""}
|