@o3co/auth.policy-verifier.builtins 0.3.0 → 0.4.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.
Files changed (133) hide show
  1. package/README.ja.md +73 -16
  2. package/README.md +76 -15
  3. package/dist/collectors/PayloadScopeCollector.d.mts +5 -0
  4. package/dist/collectors/PayloadScopeCollector.d.mts.map +1 -1
  5. package/dist/collectors/PayloadScopeCollector.mjs +8 -1
  6. package/dist/collectors/PayloadSubjectIdCollector.d.mts +9 -0
  7. package/dist/collectors/PayloadSubjectIdCollector.d.mts.map +1 -1
  8. package/dist/collectors/PayloadSubjectIdCollector.mjs +16 -4
  9. package/dist/collectors/RequestContextAttributeCollector.d.mts +47 -0
  10. package/dist/collectors/RequestContextAttributeCollector.d.mts.map +1 -0
  11. package/dist/collectors/RequestContextAttributeCollector.mjs +100 -0
  12. package/dist/collectors/StaticPermissionCollector.d.mts +5 -0
  13. package/dist/collectors/StaticPermissionCollector.d.mts.map +1 -1
  14. package/dist/collectors/StaticPermissionCollector.mjs +7 -0
  15. package/dist/collectors/StaticRoleCollector.d.mts +5 -0
  16. package/dist/collectors/StaticRoleCollector.d.mts.map +1 -1
  17. package/dist/collectors/StaticRoleCollector.mjs +7 -0
  18. package/dist/index.d.mts +4 -3
  19. package/dist/index.d.mts.map +1 -1
  20. package/dist/index.mjs +5 -2
  21. package/dist/module.d.mts +5 -0
  22. package/dist/module.d.mts.map +1 -1
  23. package/dist/module.mjs +12 -3
  24. package/dist/resource/DotNotationResourceParser.d.mts +48 -0
  25. package/dist/resource/DotNotationResourceParser.d.mts.map +1 -1
  26. package/dist/resource/DotNotationResourceParser.mjs +94 -10
  27. package/dist/rules/AttrLiteralCompare.d.mts +2 -2
  28. package/dist/rules/AttrLiteralCompare.d.mts.map +1 -1
  29. package/dist/rules/AttrLiteralCompare.mjs +2 -0
  30. package/dist/rules/AttrLiteralEqual.d.mts +2 -2
  31. package/dist/rules/AttrLiteralEqual.d.mts.map +1 -1
  32. package/dist/rules/AttrLiteralEqual.mjs +2 -0
  33. package/dist/rules/AttrLiteralIn.d.mts +2 -2
  34. package/dist/rules/AttrLiteralIn.d.mts.map +1 -1
  35. package/dist/rules/AttrLiteralIn.mjs +2 -0
  36. package/dist/rules/AttrLiteralNotEqual.d.mts +2 -2
  37. package/dist/rules/AttrLiteralNotEqual.d.mts.map +1 -1
  38. package/dist/rules/AttrLiteralNotEqual.mjs +2 -0
  39. package/dist/rules/AttrLiteralNotIn.d.mts +2 -2
  40. package/dist/rules/AttrLiteralNotIn.d.mts.map +1 -1
  41. package/dist/rules/AttrLiteralNotIn.mjs +2 -0
  42. package/dist/rules/AttrMatchRule.d.mts.map +1 -1
  43. package/dist/rules/AttrMatchRule.mjs +2 -0
  44. package/dist/rules/AttrPairCompare.d.mts +2 -2
  45. package/dist/rules/AttrPairCompare.d.mts.map +1 -1
  46. package/dist/rules/AttrPairCompare.mjs +2 -0
  47. package/dist/rules/AttrPairEqual.d.mts +2 -2
  48. package/dist/rules/AttrPairEqual.d.mts.map +1 -1
  49. package/dist/rules/AttrPairEqual.mjs +2 -0
  50. package/dist/rules/AttrPairNotEqual.d.mts +2 -2
  51. package/dist/rules/AttrPairNotEqual.d.mts.map +1 -1
  52. package/dist/rules/AttrPairNotEqual.mjs +2 -0
  53. package/dist/rules/HasPermission.d.mts +30 -2
  54. package/dist/rules/HasPermission.d.mts.map +1 -1
  55. package/dist/rules/HasPermission.mjs +30 -2
  56. package/dist/rules/HasScope.d.mts +45 -4
  57. package/dist/rules/HasScope.d.mts.map +1 -1
  58. package/dist/rules/HasScope.mjs +46 -9
  59. package/dist/rules/_sharedValidation.d.mts +12 -9
  60. package/dist/rules/_sharedValidation.d.mts.map +1 -1
  61. package/dist/rules/_sharedValidation.mjs +46 -9
  62. package/dist/rules/{ResourceActionPermissionRuleCollector.d.mts → collectors/ResourceActionPermissionRuleCollector.d.mts} +6 -0
  63. package/dist/rules/collectors/ResourceActionPermissionRuleCollector.d.mts.map +1 -0
  64. package/dist/rules/collectors/ResourceActionPermissionRuleCollector.mjs +15 -0
  65. package/dist/rules/collectors/ResourceActionScopeRuleCollector.d.mts +62 -0
  66. package/dist/rules/collectors/ResourceActionScopeRuleCollector.d.mts.map +1 -0
  67. package/dist/rules/collectors/ResourceActionScopeRuleCollector.mjs +67 -0
  68. package/package.json +8 -5
  69. package/dist/__tests__/collectors/PayloadScopeCollector.test.d.mts +0 -2
  70. package/dist/__tests__/collectors/PayloadScopeCollector.test.d.mts.map +0 -1
  71. package/dist/__tests__/collectors/PayloadScopeCollector.test.mjs +0 -24
  72. package/dist/__tests__/collectors/PayloadSubjectIdCollector.test.d.mts +0 -2
  73. package/dist/__tests__/collectors/PayloadSubjectIdCollector.test.d.mts.map +0 -1
  74. package/dist/__tests__/collectors/PayloadSubjectIdCollector.test.mjs +0 -33
  75. package/dist/__tests__/collectors/StaticPermissionCollector.test.d.mts +0 -2
  76. package/dist/__tests__/collectors/StaticPermissionCollector.test.d.mts.map +0 -1
  77. package/dist/__tests__/collectors/StaticPermissionCollector.test.mjs +0 -22
  78. package/dist/__tests__/collectors/StaticRoleCollector.test.d.mts +0 -2
  79. package/dist/__tests__/collectors/StaticRoleCollector.test.d.mts.map +0 -1
  80. package/dist/__tests__/collectors/StaticRoleCollector.test.mjs +0 -24
  81. package/dist/__tests__/module.test.d.mts +0 -2
  82. package/dist/__tests__/module.test.d.mts.map +0 -1
  83. package/dist/__tests__/module.test.mjs +0 -71
  84. package/dist/__tests__/resource/DotNotationResourceParser.test.d.mts +0 -2
  85. package/dist/__tests__/resource/DotNotationResourceParser.test.d.mts.map +0 -1
  86. package/dist/__tests__/resource/DotNotationResourceParser.test.mjs +0 -34
  87. package/dist/__tests__/rules/AttrLiteralCompare.test.d.mts +0 -2
  88. package/dist/__tests__/rules/AttrLiteralCompare.test.d.mts.map +0 -1
  89. package/dist/__tests__/rules/AttrLiteralCompare.test.mjs +0 -253
  90. package/dist/__tests__/rules/AttrLiteralEqual.test.d.mts +0 -2
  91. package/dist/__tests__/rules/AttrLiteralEqual.test.d.mts.map +0 -1
  92. package/dist/__tests__/rules/AttrLiteralEqual.test.mjs +0 -138
  93. package/dist/__tests__/rules/AttrLiteralIn.test.d.mts +0 -2
  94. package/dist/__tests__/rules/AttrLiteralIn.test.d.mts.map +0 -1
  95. package/dist/__tests__/rules/AttrLiteralIn.test.mjs +0 -160
  96. package/dist/__tests__/rules/AttrLiteralNotEqual.test.d.mts +0 -2
  97. package/dist/__tests__/rules/AttrLiteralNotEqual.test.d.mts.map +0 -1
  98. package/dist/__tests__/rules/AttrLiteralNotEqual.test.mjs +0 -157
  99. package/dist/__tests__/rules/AttrLiteralNotIn.test.d.mts +0 -2
  100. package/dist/__tests__/rules/AttrLiteralNotIn.test.d.mts.map +0 -1
  101. package/dist/__tests__/rules/AttrLiteralNotIn.test.mjs +0 -168
  102. package/dist/__tests__/rules/AttrMatchRule.test.d.mts +0 -2
  103. package/dist/__tests__/rules/AttrMatchRule.test.d.mts.map +0 -1
  104. package/dist/__tests__/rules/AttrMatchRule.test.mjs +0 -79
  105. package/dist/__tests__/rules/AttrPairCompare.test.d.mts +0 -2
  106. package/dist/__tests__/rules/AttrPairCompare.test.d.mts.map +0 -1
  107. package/dist/__tests__/rules/AttrPairCompare.test.mjs +0 -319
  108. package/dist/__tests__/rules/AttrPairEqual.test.d.mts +0 -2
  109. package/dist/__tests__/rules/AttrPairEqual.test.d.mts.map +0 -1
  110. package/dist/__tests__/rules/AttrPairEqual.test.mjs +0 -155
  111. package/dist/__tests__/rules/AttrPairNotEqual.test.d.mts +0 -2
  112. package/dist/__tests__/rules/AttrPairNotEqual.test.d.mts.map +0 -1
  113. package/dist/__tests__/rules/AttrPairNotEqual.test.mjs +0 -155
  114. package/dist/__tests__/rules/HasPermission.test.d.mts +0 -2
  115. package/dist/__tests__/rules/HasPermission.test.d.mts.map +0 -1
  116. package/dist/__tests__/rules/HasPermission.test.mjs +0 -86
  117. package/dist/__tests__/rules/HasScope.test.d.mts +0 -2
  118. package/dist/__tests__/rules/HasScope.test.d.mts.map +0 -1
  119. package/dist/__tests__/rules/HasScope.test.mjs +0 -42
  120. package/dist/__tests__/rules/ResourceActionPermissionRuleCollector.test.d.mts +0 -2
  121. package/dist/__tests__/rules/ResourceActionPermissionRuleCollector.test.d.mts.map +0 -1
  122. package/dist/__tests__/rules/ResourceActionPermissionRuleCollector.test.mjs +0 -27
  123. package/dist/__tests__/rules/ResourceActionScopeRuleCollector.test.d.mts +0 -2
  124. package/dist/__tests__/rules/ResourceActionScopeRuleCollector.test.d.mts.map +0 -1
  125. package/dist/__tests__/rules/ResourceActionScopeRuleCollector.test.mjs +0 -48
  126. package/dist/__tests__/rules/_sharedValidation.test.d.mts +0 -2
  127. package/dist/__tests__/rules/_sharedValidation.test.d.mts.map +0 -1
  128. package/dist/__tests__/rules/_sharedValidation.test.mjs +0 -325
  129. package/dist/rules/ResourceActionPermissionRuleCollector.d.mts.map +0 -1
  130. package/dist/rules/ResourceActionPermissionRuleCollector.mjs +0 -7
  131. package/dist/rules/ResourceActionScopeRuleCollector.d.mts +0 -14
  132. package/dist/rules/ResourceActionScopeRuleCollector.d.mts.map +0 -1
  133. package/dist/rules/ResourceActionScopeRuleCollector.mjs +0 -19
package/dist/index.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { PayloadScopeCollector } from "./collectors/PayloadScopeCollector.mjs";
2
2
  export { PayloadSubjectIdCollector } from "./collectors/PayloadSubjectIdCollector.mjs";
3
+ export { RequestContextAttributeCollector, type RequestContextAttributeCollectorConfig, type RequestContextAttributeMapping, type RequestContextAttributeType, } from "./collectors/RequestContextAttributeCollector.mjs";
3
4
  export { StaticPermissionCollector } from "./collectors/StaticPermissionCollector.mjs";
4
5
  export { StaticRoleCollector } from "./collectors/StaticRoleCollector.mjs";
5
6
  export { builtinCollectorsModule } from "./module.mjs";
@@ -13,8 +14,8 @@ export { AttrMatchRule, type AttrMatchRuleConfig } from "./rules/AttrMatchRule.m
13
14
  export { AttrPairCompare, type AttrPairCompareConfig } from "./rules/AttrPairCompare.mjs";
14
15
  export { AttrPairEqual, type AttrPairEqualConfig } from "./rules/AttrPairEqual.mjs";
15
16
  export { AttrPairNotEqual, type AttrPairNotEqualConfig } from "./rules/AttrPairNotEqual.mjs";
17
+ export { ResourceActionPermissionRuleCollector } from "./rules/collectors/ResourceActionPermissionRuleCollector.mjs";
18
+ export { ResourceActionScopeRuleCollector, type ResourceActionScopeRuleCollectorConfig, type ScopelessPolicy, } from "./rules/collectors/ResourceActionScopeRuleCollector.mjs";
16
19
  export { HasPermission } from "./rules/HasPermission.mjs";
17
- export { HasScope } from "./rules/HasScope.mjs";
18
- export { ResourceActionPermissionRuleCollector } from "./rules/ResourceActionPermissionRuleCollector.mjs";
19
- export { ResourceActionScopeRuleCollector } from "./rules/ResourceActionScopeRuleCollector.mjs";
20
+ export { HasScope, type HasScopeOptions } from "./rules/HasScope.mjs";
20
21
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAC/E,OAAO,EAAE,yBAAyB,EAAE,MAAM,4CAA4C,CAAC;AACvF,OAAO,EAAE,yBAAyB,EAAE,MAAM,4CAA4C,CAAC;AACvF,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAE3E,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAEvD,OAAO,EAAE,yBAAyB,EAAE,MAAM,0CAA0C,CAAC;AAErF,OAAO,EAAE,kBAAkB,EAAE,KAAK,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AACnG,OAAO,EAAE,gBAAgB,EAAE,KAAK,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EACN,mBAAmB,EACnB,KAAK,yBAAyB,GAC9B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,KAAK,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAE,eAAe,EAAE,KAAK,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAC1F,OAAO,EAAE,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAE,gBAAgB,EAAE,KAAK,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,qCAAqC,EAAE,MAAM,mDAAmD,CAAC;AAC1G,OAAO,EAAE,gCAAgC,EAAE,MAAM,8CAA8C,CAAC"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.mts"],"names":[],"mappings":"AAIA,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAC/E,OAAO,EAAE,yBAAyB,EAAE,MAAM,4CAA4C,CAAC;AACvF,OAAO,EACN,gCAAgC,EAChC,KAAK,sCAAsC,EAC3C,KAAK,8BAA8B,EACnC,KAAK,2BAA2B,GAChC,MAAM,mDAAmD,CAAC;AAC3D,OAAO,EAAE,yBAAyB,EAAE,MAAM,4CAA4C,CAAC;AACvF,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAE3E,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAEvD,OAAO,EAAE,yBAAyB,EAAE,MAAM,0CAA0C,CAAC;AAErF,OAAO,EAAE,kBAAkB,EAAE,KAAK,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AACnG,OAAO,EAAE,gBAAgB,EAAE,KAAK,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EACN,mBAAmB,EACnB,KAAK,yBAAyB,GAC9B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,KAAK,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAE,eAAe,EAAE,KAAK,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAC1F,OAAO,EAAE,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAE,gBAAgB,EAAE,KAAK,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC7F,OAAO,EAAE,qCAAqC,EAAE,MAAM,8DAA8D,CAAC;AACrH,OAAO,EACN,gCAAgC,EAChC,KAAK,sCAAsC,EAC3C,KAAK,eAAe,GACpB,MAAM,yDAAyD,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
package/dist/index.mjs CHANGED
@@ -1,6 +1,9 @@
1
+ // SPDX-FileCopyrightText: 2026 1o1 Co. Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  // Collectors
2
4
  export { PayloadScopeCollector } from "./collectors/PayloadScopeCollector.mjs";
3
5
  export { PayloadSubjectIdCollector } from "./collectors/PayloadSubjectIdCollector.mjs";
6
+ export { RequestContextAttributeCollector, } from "./collectors/RequestContextAttributeCollector.mjs";
4
7
  export { StaticPermissionCollector } from "./collectors/StaticPermissionCollector.mjs";
5
8
  export { StaticRoleCollector } from "./collectors/StaticRoleCollector.mjs";
6
9
  // Module
@@ -17,7 +20,7 @@ export { AttrMatchRule } from "./rules/AttrMatchRule.mjs";
17
20
  export { AttrPairCompare } from "./rules/AttrPairCompare.mjs";
18
21
  export { AttrPairEqual } from "./rules/AttrPairEqual.mjs";
19
22
  export { AttrPairNotEqual } from "./rules/AttrPairNotEqual.mjs";
23
+ export { ResourceActionPermissionRuleCollector } from "./rules/collectors/ResourceActionPermissionRuleCollector.mjs";
24
+ export { ResourceActionScopeRuleCollector, } from "./rules/collectors/ResourceActionScopeRuleCollector.mjs";
20
25
  export { HasPermission } from "./rules/HasPermission.mjs";
21
26
  export { HasScope } from "./rules/HasScope.mjs";
22
- export { ResourceActionPermissionRuleCollector } from "./rules/ResourceActionPermissionRuleCollector.mjs";
23
- export { ResourceActionScopeRuleCollector } from "./rules/ResourceActionScopeRuleCollector.mjs";
package/dist/module.d.mts CHANGED
@@ -1,3 +1,8 @@
1
1
  import type { Module } from "@o3co/auth.policy-verifier.core";
2
+ /**
3
+ * `Module` that registers the built-in attribute collectors, rule collectors,
4
+ * and resource parser with a policy-verifier server. Import and pass to
5
+ * `createApp({ modules: [builtinCollectorsModule, ...] })`.
6
+ */
2
7
  export declare const builtinCollectorsModule: Module;
3
8
  //# sourceMappingURL=module.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.mts","sourceRoot":"","sources":["../src/module.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAS9D,eAAO,MAAM,uBAAuB,EAAE,MAqCrC,CAAC"}
1
+ {"version":3,"file":"module.d.mts","sourceRoot":"","sources":["../src/module.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAU9D;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAyCrC,CAAC"}
package/dist/module.mjs CHANGED
@@ -1,10 +1,18 @@
1
+ // SPDX-FileCopyrightText: 2026 1o1 Co. Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  import { PayloadScopeCollector } from "./collectors/PayloadScopeCollector.mjs";
2
4
  import { PayloadSubjectIdCollector } from "./collectors/PayloadSubjectIdCollector.mjs";
5
+ import { RequestContextAttributeCollector } from "./collectors/RequestContextAttributeCollector.mjs";
3
6
  import { StaticPermissionCollector } from "./collectors/StaticPermissionCollector.mjs";
4
7
  import { StaticRoleCollector } from "./collectors/StaticRoleCollector.mjs";
5
8
  import { DotNotationResourceParser } from "./resource/DotNotationResourceParser.mjs";
6
- import { ResourceActionPermissionRuleCollector } from "./rules/ResourceActionPermissionRuleCollector.mjs";
7
- import { ResourceActionScopeRuleCollector } from "./rules/ResourceActionScopeRuleCollector.mjs";
9
+ import { ResourceActionPermissionRuleCollector } from "./rules/collectors/ResourceActionPermissionRuleCollector.mjs";
10
+ import { ResourceActionScopeRuleCollector } from "./rules/collectors/ResourceActionScopeRuleCollector.mjs";
11
+ /**
12
+ * `Module` that registers the built-in attribute collectors, rule collectors,
13
+ * and resource parser with a policy-verifier server. Import and pass to
14
+ * `createApp({ modules: [builtinCollectorsModule, ...] })`.
15
+ */
8
16
  export const builtinCollectorsModule = {
9
17
  name: "builtin-collectors",
10
18
  async init(context) {
@@ -13,8 +21,9 @@ export const builtinCollectorsModule = {
13
21
  context.attributeCollectorRegistry.register("PayloadSubjectIdCollector", () => new PayloadSubjectIdCollector());
14
22
  context.attributeCollectorRegistry.register("StaticPermissionCollector", (config) => new StaticPermissionCollector(config));
15
23
  context.attributeCollectorRegistry.register("StaticRoleCollector", (config) => new StaticRoleCollector(config));
24
+ context.attributeCollectorRegistry.register("RequestContextAttributeCollector", (config) => new RequestContextAttributeCollector(config));
16
25
  // Rule collector factories
17
- context.ruleCollectorRegistry.register("ResourceActionScopeRuleCollector", () => new ResourceActionScopeRuleCollector());
26
+ context.ruleCollectorRegistry.register("ResourceActionScopeRuleCollector", (config) => new ResourceActionScopeRuleCollector(config));
18
27
  context.ruleCollectorRegistry.register("ResourceActionPermissionRuleCollector", () => new ResourceActionPermissionRuleCollector());
19
28
  // Resource parser factories
20
29
  context.resourceParserRegistry.register("DotNotationResourceParser", () => new DotNotationResourceParser());
@@ -1,4 +1,52 @@
1
1
  import type { Resource, ResourceParser } from "@o3co/auth.policy-verifier.core";
2
+ /**
3
+ * Resource parser for dot-separated, colon-qualified identifiers such as
4
+ * `"org:123.project:abc.document:42"`.
5
+ *
6
+ * ## Grammar
7
+ *
8
+ * ```text
9
+ * resource = segment *( "." segment )
10
+ * segment = type [ ":" id ]
11
+ * type = 1*tchar
12
+ * id = 1*tchar
13
+ * tchar = %x21 / %x23-2D / %x2F-39 / %x3B-5B / %x5D-7E
14
+ * ; RFC 6749 NQCHAR less "." and ":"
15
+ * ```
16
+ *
17
+ * `resourceType` is the segment types joined with `.`; `resourceId` is the id
18
+ * of the last segment, if it has one. Anything the grammar does not accept
19
+ * raises a {@link ResourceParseError} — the parser never repairs its input.
20
+ *
21
+ * ## Why nothing is rewritten
22
+ *
23
+ * `resourceType` is the authorization namespace: scope rules authorize it, so
24
+ * two distinct resources that parse to the same type are authorized
25
+ * identically, and a caller can reach resource A through a grant written for
26
+ * resource B. Every rewrite the parser could perform is a way for that to
27
+ * happen, so it performs none:
28
+ *
29
+ * - **The `.` separator is preserved.** Joining types with `_` collapsed the
30
+ * nested type `a.b` and the flat type literally named `a_b` onto one string.
31
+ * `.` is reserved as the separator and cannot occur inside a type, so the
32
+ * sequence of types round-trips: distinct type sequences are distinct
33
+ * `resourceType`s. `_` is now an ordinary type character.
34
+ * - **Empty segments are refused.** `a..b` used to parse, and every input with
35
+ * a repeated `.` landed in whatever namespace the empty types produced.
36
+ * - **Extra `:` components are refused, not truncated.** `a:1:2` used to be
37
+ * read as `a:1` with the tail silently dropped, so a deliberately narrowed
38
+ * identifier widened into the broader one.
39
+ * - **Whitespace is refused, not trimmed.** Trimming made `project : 1` and
40
+ * `project:1` one resource for scope rules while
41
+ * `ResourceActionPermissionRuleCollector`, which reads `raw`, still saw two.
42
+ *
43
+ * This is the same principle `HasScope` applies to scope values: compare what
44
+ * was written, never a normalized guess at what was meant.
45
+ *
46
+ * An id that needs `.`, `:` or a character outside the set must be encoded by
47
+ * the caller (percent-encoding round-trips through this grammar) or handled by
48
+ * a `ResourceParser` written for that syntax.
49
+ */
2
50
  export declare class DotNotationResourceParser implements ResourceParser {
3
51
  parse(raw: string): Resource;
4
52
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DotNotationResourceParser.d.mts","sourceRoot":"","sources":["../../src/resource/DotNotationResourceParser.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEhF,qBAAa,yBAA0B,YAAW,cAAc;IAC/D,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ;CAc5B"}
1
+ {"version":3,"file":"DotNotationResourceParser.d.mts","sourceRoot":"","sources":["../../src/resource/DotNotationResourceParser.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAqBhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,qBAAa,yBAA0B,YAAW,cAAc;IAC/D,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ;CAY5B"}
@@ -1,14 +1,98 @@
1
+ // SPDX-FileCopyrightText: 2026 1o1 Co. Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { ResourceParseError } from "@o3co/auth.policy-verifier.core";
4
+ /**
5
+ * Characters a segment type or id may carry.
6
+ *
7
+ * The set is RFC 6749 §3.3 `NQCHAR` (`%x21 / %x23-5B / %x5D-7E` — printable
8
+ * ASCII minus space, `"` and `\`) less the two structural characters `.` and
9
+ * `:`. Anchoring it there is not arbitrary: `resourceType` is concatenated into
10
+ * the `{action}:{resourceType}` scope that `ResourceActionScopeRuleCollector`
11
+ * requires, so a type that cannot appear in a scope value is a type no issuer
12
+ * could ever grant.
13
+ */
14
+ const SEGMENT_TOKEN = /^[\x21\x23-\x2D\x2F-\x39\x3B-\x5B\x5D-\x7E]+$/;
15
+ /**
16
+ * Resource parser for dot-separated, colon-qualified identifiers such as
17
+ * `"org:123.project:abc.document:42"`.
18
+ *
19
+ * ## Grammar
20
+ *
21
+ * ```text
22
+ * resource = segment *( "." segment )
23
+ * segment = type [ ":" id ]
24
+ * type = 1*tchar
25
+ * id = 1*tchar
26
+ * tchar = %x21 / %x23-2D / %x2F-39 / %x3B-5B / %x5D-7E
27
+ * ; RFC 6749 NQCHAR less "." and ":"
28
+ * ```
29
+ *
30
+ * `resourceType` is the segment types joined with `.`; `resourceId` is the id
31
+ * of the last segment, if it has one. Anything the grammar does not accept
32
+ * raises a {@link ResourceParseError} — the parser never repairs its input.
33
+ *
34
+ * ## Why nothing is rewritten
35
+ *
36
+ * `resourceType` is the authorization namespace: scope rules authorize it, so
37
+ * two distinct resources that parse to the same type are authorized
38
+ * identically, and a caller can reach resource A through a grant written for
39
+ * resource B. Every rewrite the parser could perform is a way for that to
40
+ * happen, so it performs none:
41
+ *
42
+ * - **The `.` separator is preserved.** Joining types with `_` collapsed the
43
+ * nested type `a.b` and the flat type literally named `a_b` onto one string.
44
+ * `.` is reserved as the separator and cannot occur inside a type, so the
45
+ * sequence of types round-trips: distinct type sequences are distinct
46
+ * `resourceType`s. `_` is now an ordinary type character.
47
+ * - **Empty segments are refused.** `a..b` used to parse, and every input with
48
+ * a repeated `.` landed in whatever namespace the empty types produced.
49
+ * - **Extra `:` components are refused, not truncated.** `a:1:2` used to be
50
+ * read as `a:1` with the tail silently dropped, so a deliberately narrowed
51
+ * identifier widened into the broader one.
52
+ * - **Whitespace is refused, not trimmed.** Trimming made `project : 1` and
53
+ * `project:1` one resource for scope rules while
54
+ * `ResourceActionPermissionRuleCollector`, which reads `raw`, still saw two.
55
+ *
56
+ * This is the same principle `HasScope` applies to scope values: compare what
57
+ * was written, never a normalized guess at what was meant.
58
+ *
59
+ * An id that needs `.`, `:` or a character outside the set must be encoded by
60
+ * the caller (percent-encoding round-trips through this grammar) or handled by
61
+ * a `ResourceParser` written for that syntax.
62
+ */
1
63
  export class DotNotationResourceParser {
2
64
  parse(raw) {
3
- const segments = raw
4
- .trim()
5
- .split(".")
6
- .map((part) => {
7
- const [type, id] = part.split(":").map((s) => s.trim());
8
- return { type, id: id || undefined };
9
- });
10
- const resourceType = segments.map((s) => s.type).join("_");
11
- const resourceId = segments[segments.length - 1].id;
12
- return { raw, resourceType, resourceId };
65
+ const segments = raw.split(".").map((segment, index) => parseSegment(raw, segment, index + 1));
66
+ return {
67
+ raw,
68
+ // Safe because `.` cannot occur inside a type: the join is injective
69
+ // over the sequence of types.
70
+ resourceType: segments.map((segment) => segment.type).join("."),
71
+ // `split` always yields at least one element, so there is a last segment.
72
+ resourceId: segments[segments.length - 1].id,
73
+ };
74
+ }
75
+ }
76
+ /** Validates one `type[:id]` segment, or explains why it is not one. */
77
+ function parseSegment(raw, segment, position) {
78
+ const parts = segment.split(":");
79
+ if (parts.length > 2) {
80
+ throw new ResourceParseError(raw, `segment ${position} has more than one ":"; a segment is "type" or "type:id"`);
81
+ }
82
+ const [type, id] = parts;
83
+ assertToken(raw, type, position, "type");
84
+ if (id !== undefined) {
85
+ assertToken(raw, id, position, "id");
86
+ }
87
+ return id === undefined ? { type } : { type, id };
88
+ }
89
+ /** Rejects an empty or out-of-grammar type/id, naming which part of which segment. */
90
+ function assertToken(raw, value, position, part) {
91
+ if (value === "") {
92
+ throw new ResourceParseError(raw, `segment ${position} has an empty ${part}`);
93
+ }
94
+ if (!SEGMENT_TOKEN.test(value)) {
95
+ throw new ResourceParseError(raw, `segment ${position} ${part} carries a character outside the allowed set ` +
96
+ `(printable ASCII except space, '"', "\\", "." and ":")`);
13
97
  }
14
98
  }
@@ -1,4 +1,4 @@
1
- import type { Attributes, Rule } from "@o3co/auth.policy-verifier.core";
1
+ import type { ReadonlyAttributes, Rule } from "@o3co/auth.policy-verifier.core";
2
2
  import { type CompareOp } from "./_sharedValidation.mjs";
3
3
  export interface AttrLiteralCompareConfig {
4
4
  a: string;
@@ -26,6 +26,6 @@ export declare class AttrLiteralCompare implements Rule {
26
26
  readonly code = "attr_compare_violated";
27
27
  readonly message: string;
28
28
  constructor(config: AttrLiteralCompareConfig);
29
- verify(attrs: Attributes): boolean;
29
+ verify(attrs: ReadonlyAttributes): boolean;
30
30
  }
31
31
  //# sourceMappingURL=AttrLiteralCompare.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AttrLiteralCompare.d.mts","sourceRoot":"","sources":["../../src/rules/AttrLiteralCompare.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAEN,KAAK,SAAS,EAKd,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,wBAAwB;IACxC,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,SAAS,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,kBAAmB,YAAW,IAAI;IAKlC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,2BAA2B;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEI,MAAM,EAAE,wBAAwB;IAU7D,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO;CAKlC"}
1
+ {"version":3,"file":"AttrLiteralCompare.d.mts","sourceRoot":"","sources":["../../src/rules/AttrLiteralCompare.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EAEN,KAAK,SAAS,EAKd,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,wBAAwB;IACxC,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,SAAS,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,kBAAmB,YAAW,IAAI;IAKlC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,2BAA2B;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEI,MAAM,EAAE,wBAAwB;IAU7D,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO;CAK1C"}
@@ -1,3 +1,5 @@
1
+ // SPDX-FileCopyrightText: 2026 1o1 Co. Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  import { applyCompare, requireAttrName, requireCompareOp, requireNumber, requireOptionalGroup, } from "./_sharedValidation.mjs";
2
4
  /**
3
5
  * Rule that passes when a named attribute is present, is a number, and
@@ -1,4 +1,4 @@
1
- import type { Attributes, Rule } from "@o3co/auth.policy-verifier.core";
1
+ import type { ReadonlyAttributes, Rule } from "@o3co/auth.policy-verifier.core";
2
2
  import { type LiteralValue } from "./_sharedValidation.mjs";
3
3
  export interface AttrLiteralEqualConfig {
4
4
  a: string;
@@ -32,6 +32,6 @@ export declare class AttrLiteralEqual implements Rule {
32
32
  readonly code = "attr_not_equal";
33
33
  readonly message: string;
34
34
  constructor(config: AttrLiteralEqualConfig);
35
- verify(attrs: Attributes): boolean;
35
+ verify(attrs: ReadonlyAttributes): boolean;
36
36
  }
37
37
  //# sourceMappingURL=AttrLiteralEqual.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AttrLiteralEqual.d.mts","sourceRoot":"","sources":["../../src/rules/AttrLiteralEqual.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EACN,KAAK,YAAY,EAIjB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,sBAAsB;IACtC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,YAAY,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,gBAAiB,YAAW,IAAI;IAKhC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,oBAAoB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEI,MAAM,EAAE,sBAAsB;IAU3D,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO;CAKlC"}
1
+ {"version":3,"file":"AttrLiteralEqual.d.mts","sourceRoot":"","sources":["../../src/rules/AttrLiteralEqual.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EACN,KAAK,YAAY,EAIjB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,sBAAsB;IACtC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,YAAY,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,gBAAiB,YAAW,IAAI;IAKhC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,oBAAoB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEI,MAAM,EAAE,sBAAsB;IAU3D,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO;CAK1C"}
@@ -1,3 +1,5 @@
1
+ // SPDX-FileCopyrightText: 2026 1o1 Co. Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  import { requireAttrName, requireLiteralValue, requireOptionalGroup, } from "./_sharedValidation.mjs";
2
4
  /**
3
5
  * Rule that passes when a named attribute is present and strictly equal to a
@@ -1,4 +1,4 @@
1
- import type { Attributes, Rule } from "@o3co/auth.policy-verifier.core";
1
+ import type { ReadonlyAttributes, Rule } from "@o3co/auth.policy-verifier.core";
2
2
  import { type LiteralValue } from "./_sharedValidation.mjs";
3
3
  export interface AttrLiteralInConfig {
4
4
  a: string;
@@ -33,6 +33,6 @@ export declare class AttrLiteralIn implements Rule {
33
33
  private readonly elementType;
34
34
  private readonly valuesSet;
35
35
  constructor(config: AttrLiteralInConfig);
36
- verify(attrs: Attributes): boolean;
36
+ verify(attrs: ReadonlyAttributes): boolean;
37
37
  }
38
38
  //# sourceMappingURL=AttrLiteralIn.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AttrLiteralIn.d.mts","sourceRoot":"","sources":["../../src/rules/AttrLiteralIn.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAEN,KAAK,YAAY,EAIjB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,mBAAmB;IACnC,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,aAAc,YAAW,IAAI;IAQ7B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,qBAAqB;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;gBAEjB,MAAM,EAAE,mBAAmB;IAaxD,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO;CAKlC"}
1
+ {"version":3,"file":"AttrLiteralIn.d.mts","sourceRoot":"","sources":["../../src/rules/AttrLiteralIn.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EAEN,KAAK,YAAY,EAIjB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,mBAAmB;IACnC,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,aAAc,YAAW,IAAI;IAQ7B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,qBAAqB;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;gBAEjB,MAAM,EAAE,mBAAmB;IAaxD,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO;CAK1C"}
@@ -1,3 +1,5 @@
1
+ // SPDX-FileCopyrightText: 2026 1o1 Co. Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  import { computeValuesKey, requireAttrName, requireHomogeneousLiteralArray, requireOptionalGroup, } from "./_sharedValidation.mjs";
2
4
  /**
3
5
  * Rule that passes when a named attribute is present, matches the element type
@@ -1,4 +1,4 @@
1
- import type { Attributes, Rule } from "@o3co/auth.policy-verifier.core";
1
+ import type { ReadonlyAttributes, Rule } from "@o3co/auth.policy-verifier.core";
2
2
  import { type LiteralValue } from "./_sharedValidation.mjs";
3
3
  export interface AttrLiteralNotEqualConfig {
4
4
  a: string;
@@ -31,6 +31,6 @@ export declare class AttrLiteralNotEqual implements Rule {
31
31
  readonly code = "attr_equal";
32
32
  readonly message: string;
33
33
  constructor(config: AttrLiteralNotEqualConfig);
34
- verify(attrs: Attributes): boolean;
34
+ verify(attrs: ReadonlyAttributes): boolean;
35
35
  }
36
36
  //# sourceMappingURL=AttrLiteralNotEqual.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AttrLiteralNotEqual.d.mts","sourceRoot":"","sources":["../../src/rules/AttrLiteralNotEqual.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EACN,KAAK,YAAY,EAIjB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,yBAAyB;IACzC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,YAAY,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,mBAAoB,YAAW,IAAI;IAKnC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,gBAAgB;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEI,MAAM,EAAE,yBAAyB;IAU9D,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO;CAKlC"}
1
+ {"version":3,"file":"AttrLiteralNotEqual.d.mts","sourceRoot":"","sources":["../../src/rules/AttrLiteralNotEqual.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EACN,KAAK,YAAY,EAIjB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,yBAAyB;IACzC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,YAAY,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,mBAAoB,YAAW,IAAI;IAKnC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,gBAAgB;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEI,MAAM,EAAE,yBAAyB;IAU9D,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO;CAK1C"}
@@ -1,3 +1,5 @@
1
+ // SPDX-FileCopyrightText: 2026 1o1 Co. Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  import { requireAttrName, requireLiteralValue, requireOptionalGroup, } from "./_sharedValidation.mjs";
2
4
  /**
3
5
  * Rule that passes when a named attribute is present, matches the type of the
@@ -1,4 +1,4 @@
1
- import type { Attributes, Rule } from "@o3co/auth.policy-verifier.core";
1
+ import type { ReadonlyAttributes, Rule } from "@o3co/auth.policy-verifier.core";
2
2
  import { type LiteralValue } from "./_sharedValidation.mjs";
3
3
  export interface AttrLiteralNotInConfig {
4
4
  a: string;
@@ -28,6 +28,6 @@ export declare class AttrLiteralNotIn implements Rule {
28
28
  private readonly elementType;
29
29
  private readonly valuesSet;
30
30
  constructor(config: AttrLiteralNotInConfig);
31
- verify(attrs: Attributes): boolean;
31
+ verify(attrs: ReadonlyAttributes): boolean;
32
32
  }
33
33
  //# sourceMappingURL=AttrLiteralNotIn.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AttrLiteralNotIn.d.mts","sourceRoot":"","sources":["../../src/rules/AttrLiteralNotIn.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAEN,KAAK,YAAY,EAIjB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,sBAAsB;IACtC,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAiB,YAAW,IAAI;IAQhC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,iBAAiB;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;gBAEjB,MAAM,EAAE,sBAAsB;IAa3D,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO;CAKlC"}
1
+ {"version":3,"file":"AttrLiteralNotIn.d.mts","sourceRoot":"","sources":["../../src/rules/AttrLiteralNotIn.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EAEN,KAAK,YAAY,EAIjB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,sBAAsB;IACtC,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAiB,YAAW,IAAI;IAQhC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,iBAAiB;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;gBAEjB,MAAM,EAAE,sBAAsB;IAa3D,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO;CAK1C"}
@@ -1,3 +1,5 @@
1
+ // SPDX-FileCopyrightText: 2026 1o1 Co. Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  import { computeValuesKey, requireAttrName, requireHomogeneousLiteralArray, requireOptionalGroup, } from "./_sharedValidation.mjs";
2
4
  /**
3
5
  * Rule that passes when a named attribute is present, matches the element type
@@ -1 +1 @@
1
- {"version":3,"file":"AttrMatchRule.d.mts","sourceRoot":"","sources":["../../src/rules/AttrMatchRule.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE9E,MAAM,MAAM,mBAAmB,GAAG,mBAAmB,CAAC;AAEtD;;;;;;GAMG;AACH,qBAAa,aAAc,SAAQ,aAAa;IAC/C,SAAkB,QAAQ,EAAE,MAAM,CAAC;IACnC,SAAkB,OAAO,EAAE,MAAM,CAAC;gBAEtB,MAAM,EAAE,mBAAmB;CAKvC"}
1
+ {"version":3,"file":"AttrMatchRule.d.mts","sourceRoot":"","sources":["../../src/rules/AttrMatchRule.mts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE9E,MAAM,MAAM,mBAAmB,GAAG,mBAAmB,CAAC;AAEtD;;;;;;GAMG;AACH,qBAAa,aAAc,SAAQ,aAAa;IAC/C,SAAkB,QAAQ,EAAE,MAAM,CAAC;IACnC,SAAkB,OAAO,EAAE,MAAM,CAAC;gBAEtB,MAAM,EAAE,mBAAmB;CAKvC"}
@@ -1,3 +1,5 @@
1
+ // SPDX-FileCopyrightText: 2026 1o1 Co. Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  import { AttrPairEqual } from "./AttrPairEqual.mjs";
2
4
  /**
3
5
  * @deprecated Use {@link AttrPairEqual}. Scheduled for removal in a future major version.
@@ -1,4 +1,4 @@
1
- import type { Attributes, Rule } from "@o3co/auth.policy-verifier.core";
1
+ import type { ReadonlyAttributes, Rule } from "@o3co/auth.policy-verifier.core";
2
2
  import { type CompareOp } from "./_sharedValidation.mjs";
3
3
  export interface AttrPairCompareConfig {
4
4
  a: string;
@@ -30,6 +30,6 @@ export declare class AttrPairCompare implements Rule {
30
30
  readonly code = "attr_compare_violated";
31
31
  readonly message: string;
32
32
  constructor(config: AttrPairCompareConfig);
33
- verify(attrs: Attributes): boolean;
33
+ verify(attrs: ReadonlyAttributes): boolean;
34
34
  }
35
35
  //# sourceMappingURL=AttrPairCompare.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AttrPairCompare.d.mts","sourceRoot":"","sources":["../../src/rules/AttrPairCompare.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAEN,KAAK,SAAS,EAId,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,qBAAqB;IACrC,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,SAAS,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,eAAgB,YAAW,IAAI;IAK/B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,2BAA2B;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEI,MAAM,EAAE,qBAAqB;IAU1D,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO;CAOlC"}
1
+ {"version":3,"file":"AttrPairCompare.d.mts","sourceRoot":"","sources":["../../src/rules/AttrPairCompare.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AAChF,OAAO,EAEN,KAAK,SAAS,EAId,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,qBAAqB;IACrC,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,SAAS,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,eAAgB,YAAW,IAAI;IAK/B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,2BAA2B;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEI,MAAM,EAAE,qBAAqB;IAU1D,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO;CAO1C"}
@@ -1,3 +1,5 @@
1
+ // SPDX-FileCopyrightText: 2026 1o1 Co. Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  import { applyCompare, requireAttrName, requireCompareOp, requireOptionalGroup, } from "./_sharedValidation.mjs";
2
4
  /**
3
5
  * Rule that passes when two named attributes are present, are both numbers,
@@ -1,4 +1,4 @@
1
- import type { Attributes, Rule } from "@o3co/auth.policy-verifier.core";
1
+ import type { ReadonlyAttributes, Rule } from "@o3co/auth.policy-verifier.core";
2
2
  export interface AttrPairEqualConfig {
3
3
  a: string;
4
4
  b: string;
@@ -30,6 +30,6 @@ export declare class AttrPairEqual implements Rule {
30
30
  readonly code = "attr_mismatch";
31
31
  readonly message: string;
32
32
  constructor(config: AttrPairEqualConfig);
33
- verify(attrs: Attributes): boolean;
33
+ verify(attrs: ReadonlyAttributes): boolean;
34
34
  }
35
35
  //# sourceMappingURL=AttrPairEqual.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AttrPairEqual.d.mts","sourceRoot":"","sources":["../../src/rules/AttrPairEqual.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AAGxE,MAAM,WAAW,mBAAmB;IACnC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,aAAc,YAAW,IAAI;IAK7B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,mBAAmB;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEI,MAAM,EAAE,mBAAmB;IASxD,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO;CAOlC"}
1
+ {"version":3,"file":"AttrPairEqual.d.mts","sourceRoot":"","sources":["../../src/rules/AttrPairEqual.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AAGhF,MAAM,WAAW,mBAAmB;IACnC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,aAAc,YAAW,IAAI;IAK7B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,mBAAmB;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEI,MAAM,EAAE,mBAAmB;IASxD,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO;CAO1C"}
@@ -1,3 +1,5 @@
1
+ // SPDX-FileCopyrightText: 2026 1o1 Co. Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  import { requireAttrName, requireOptionalGroup } from "./_sharedValidation.mjs";
2
4
  /**
3
5
  * Rule that passes when two named attributes are present, are non-empty
@@ -1,4 +1,4 @@
1
- import type { Attributes, Rule } from "@o3co/auth.policy-verifier.core";
1
+ import type { ReadonlyAttributes, Rule } from "@o3co/auth.policy-verifier.core";
2
2
  export interface AttrPairNotEqualConfig {
3
3
  a: string;
4
4
  b: string;
@@ -29,6 +29,6 @@ export declare class AttrPairNotEqual implements Rule {
29
29
  readonly code = "attr_match";
30
30
  readonly message: string;
31
31
  constructor(config: AttrPairNotEqualConfig);
32
- verify(attrs: Attributes): boolean;
32
+ verify(attrs: ReadonlyAttributes): boolean;
33
33
  }
34
34
  //# sourceMappingURL=AttrPairNotEqual.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AttrPairNotEqual.d.mts","sourceRoot":"","sources":["../../src/rules/AttrPairNotEqual.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AAGxE,MAAM,WAAW,sBAAsB;IACtC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,gBAAiB,YAAW,IAAI;IAKhC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,gBAAgB;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEI,MAAM,EAAE,sBAAsB;IAS3D,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO;CAOlC"}
1
+ {"version":3,"file":"AttrPairNotEqual.d.mts","sourceRoot":"","sources":["../../src/rules/AttrPairNotEqual.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AAGhF,MAAM,WAAW,sBAAsB;IACtC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,gBAAiB,YAAW,IAAI;IAKhC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,gBAAgB;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEI,MAAM,EAAE,sBAAsB;IAS3D,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO;CAO1C"}
@@ -1,3 +1,5 @@
1
+ // SPDX-FileCopyrightText: 2026 1o1 Co. Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  import { requireAttrName, requireOptionalGroup } from "./_sharedValidation.mjs";
2
4
  /**
3
5
  * Rule that passes when two named attributes are present, are non-empty
@@ -1,11 +1,39 @@
1
- import type { Attributes, Rule } from "@o3co/auth.policy-verifier.core";
1
+ import type { ReadonlyAttributes, Rule } from "@o3co/auth.policy-verifier.core";
2
+ /**
3
+ * Rule that passes when the subject holds the required permission, either
4
+ * directly via `ATTR_PERMISSIONS` or transitively through a role in
5
+ * `ATTR_ROLES`.
6
+ *
7
+ * ## Matching
8
+ *
9
+ * Comparison is **exact and case-sensitive**, the same philosophy `HasScope`
10
+ * applies to scope values and `DotNotationResourceParser` applies to resource
11
+ * identifiers (#116, #117): compare what was written, never a normalized guess
12
+ * at what was meant. This rule matched case-insensitively until #155, which
13
+ * put permissions on the wrong side of the line those two argued —
14
+ * `ResourceActionPermissionRuleCollector` builds `{resource.raw}.perm:{action}`
15
+ * from the case-preserving parser, so `Project:1` and `project:1` were two
16
+ * namespaces to a scope rule and one to a permission rule. One vocabulary, one
17
+ * matching discipline.
18
+ *
19
+ * ## Wildcards
20
+ *
21
+ * A granted permission of `"*"` matches everything, and a granted permission
22
+ * may contain a single `*` as prefix, suffix, or middle separator (e.g.
23
+ * `"posts.*"`, `"*.read"`, `"posts.*.read"`). This is not an exception to the
24
+ * no-normalization rule: a wildcard is match structure the policy author
25
+ * **wrote into the grant**, not a rewrite of both sides behind their back. The
26
+ * literal halves around the `*` still compare exactly and case-sensitively.
27
+ * Multiple wildcards are rejected outright because the two-part split would
28
+ * silently drop segments and over-grant.
29
+ */
2
30
  export declare class HasPermission implements Rule {
3
31
  private permission;
4
32
  readonly ruleType = "permission";
5
33
  readonly code = "no_permission";
6
34
  readonly message: string;
7
35
  constructor(permission: string);
8
- verify(attrs: Attributes): boolean;
36
+ verify(attrs: ReadonlyAttributes): boolean;
9
37
  private match;
10
38
  }
11
39
  //# sourceMappingURL=HasPermission.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"HasPermission.d.mts","sourceRoot":"","sources":["../../src/rules/HasPermission.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAQ,IAAI,EAAE,MAAM,iCAAiC,CAAC;AAG9E,qBAAa,aAAc,YAAW,IAAI;IAK7B,OAAO,CAAC,UAAU;IAJ9B,QAAQ,CAAC,QAAQ,gBAAgB;IACjC,QAAQ,CAAC,IAAI,mBAAmB;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEL,UAAU,EAAE,MAAM;IAItC,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO;IAUlC,OAAO,CAAC,KAAK;CAuBb"}
1
+ {"version":3,"file":"HasPermission.d.mts","sourceRoot":"","sources":["../../src/rules/HasPermission.mts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAQ,IAAI,EAAE,MAAM,iCAAiC,CAAC;AAGtF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,aAAc,YAAW,IAAI;IAK7B,OAAO,CAAC,UAAU;IAJ9B,QAAQ,CAAC,QAAQ,gBAAgB;IACjC,QAAQ,CAAC,IAAI,mBAAmB;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEL,UAAU,EAAE,MAAM;IAItC,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO;IAU1C,OAAO,CAAC,KAAK;CAoBb"}
@@ -1,4 +1,34 @@
1
+ // SPDX-FileCopyrightText: 2026 1o1 Co. Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
1
3
  import { ATTR_PERMISSIONS, ATTR_ROLES } from "@o3co/auth.policy-verifier.core";
4
+ /**
5
+ * Rule that passes when the subject holds the required permission, either
6
+ * directly via `ATTR_PERMISSIONS` or transitively through a role in
7
+ * `ATTR_ROLES`.
8
+ *
9
+ * ## Matching
10
+ *
11
+ * Comparison is **exact and case-sensitive**, the same philosophy `HasScope`
12
+ * applies to scope values and `DotNotationResourceParser` applies to resource
13
+ * identifiers (#116, #117): compare what was written, never a normalized guess
14
+ * at what was meant. This rule matched case-insensitively until #155, which
15
+ * put permissions on the wrong side of the line those two argued —
16
+ * `ResourceActionPermissionRuleCollector` builds `{resource.raw}.perm:{action}`
17
+ * from the case-preserving parser, so `Project:1` and `project:1` were two
18
+ * namespaces to a scope rule and one to a permission rule. One vocabulary, one
19
+ * matching discipline.
20
+ *
21
+ * ## Wildcards
22
+ *
23
+ * A granted permission of `"*"` matches everything, and a granted permission
24
+ * may contain a single `*` as prefix, suffix, or middle separator (e.g.
25
+ * `"posts.*"`, `"*.read"`, `"posts.*.read"`). This is not an exception to the
26
+ * no-normalization rule: a wildcard is match structure the policy author
27
+ * **wrote into the grant**, not a rewrite of both sides behind their back. The
28
+ * literal halves around the `*` still compare exactly and case-sensitively.
29
+ * Multiple wildcards are rejected outright because the two-part split would
30
+ * silently drop segments and over-grant.
31
+ */
2
32
  export class HasPermission {
3
33
  permission;
4
34
  ruleType = "permission";
@@ -15,8 +45,6 @@ export class HasPermission {
15
45
  return all.some((p) => this.match(p, this.permission));
16
46
  }
17
47
  match(permission, required) {
18
- permission = permission.toLowerCase();
19
- required = required.toLowerCase();
20
48
  if (permission === "*")
21
49
  return true;
22
50
  if (permission === required)