@o3co/auth.policy-verifier.builtins 0.3.1 → 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.
- 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 +30 -2
- package/dist/rules/HasPermission.d.mts.map +1 -1
- package/dist/rules/HasPermission.mjs +30 -2
- 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
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.mts"],"names":[],"mappings":"
|
|
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
|
|
@@ -18,6 +21,6 @@ export { AttrPairCompare } from "./rules/AttrPairCompare.mjs";
|
|
|
18
21
|
export { AttrPairEqual } from "./rules/AttrPairEqual.mjs";
|
|
19
22
|
export { AttrPairNotEqual } from "./rules/AttrPairNotEqual.mjs";
|
|
20
23
|
export { ResourceActionPermissionRuleCollector } from "./rules/collectors/ResourceActionPermissionRuleCollector.mjs";
|
|
21
|
-
export { ResourceActionScopeRuleCollector } from "./rules/collectors/ResourceActionScopeRuleCollector.mjs";
|
|
24
|
+
export { ResourceActionScopeRuleCollector, } from "./rules/collectors/ResourceActionScopeRuleCollector.mjs";
|
|
22
25
|
export { HasPermission } from "./rules/HasPermission.mjs";
|
|
23
26
|
export { HasScope } from "./rules/HasScope.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
|
package/dist/module.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.mts","sourceRoot":"","sources":["../src/module.mts"],"names":[],"mappings":"
|
|
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
9
|
import { ResourceActionPermissionRuleCollector } from "./rules/collectors/ResourceActionPermissionRuleCollector.mjs";
|
|
7
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":"
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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 {
|
|
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:
|
|
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":"
|
|
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 {
|
|
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:
|
|
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":"
|
|
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 {
|
|
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:
|
|
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":"
|
|
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 {
|
|
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:
|
|
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":"
|
|
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 {
|
|
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:
|
|
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":"
|
|
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":"
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
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:
|
|
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":"
|
|
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 {
|
|
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:
|
|
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":"
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
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:
|
|
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":"
|
|
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,11 +1,39 @@
|
|
|
1
|
-
import type {
|
|
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:
|
|
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":"
|
|
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)
|