@o3co/auth.policy-verifier.builtins 0.12.0 → 0.14.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 +26 -5
- package/README.md +41 -5
- package/dist/collectors/PayloadClaimAttributeCollector.d.mts.map +1 -1
- package/dist/collectors/PayloadScopeCollector.d.mts.map +1 -1
- package/dist/collectors/PayloadSubjectIdCollector.d.mts.map +1 -1
- package/dist/collectors/RequestContextAttributeCollector.d.mts.map +1 -1
- package/dist/collectors/StaticPermissionCollector.d.mts +15 -1
- package/dist/collectors/StaticPermissionCollector.d.mts.map +1 -1
- package/dist/collectors/StaticPermissionCollector.mjs +19 -1
- package/dist/collectors/StaticRoleCollector.d.mts +17 -1
- package/dist/collectors/StaticRoleCollector.d.mts.map +1 -1
- package/dist/collectors/StaticRoleCollector.mjs +40 -1
- package/dist/collectors/_attributeMapping.d.mts +10 -2
- package/dist/collectors/_attributeMapping.d.mts.map +1 -1
- package/dist/collectors/_attributeMapping.mjs +10 -2
- package/dist/collectors/_claims.d.mts.map +1 -1
- package/dist/collectors/_claims.mjs +7 -3
- package/dist/module.d.mts.map +1 -1
- package/dist/resource/DotNotationResourceParser.d.mts.map +1 -1
- package/dist/rules/AttrLiteralCompare.d.mts +11 -1
- package/dist/rules/AttrLiteralCompare.d.mts.map +1 -1
- package/dist/rules/AttrLiteralCompare.mjs +21 -9
- package/dist/rules/AttrLiteralEqual.d.mts +10 -1
- package/dist/rules/AttrLiteralEqual.d.mts.map +1 -1
- package/dist/rules/AttrLiteralEqual.mjs +19 -10
- package/dist/rules/AttrLiteralIn.d.mts +9 -1
- package/dist/rules/AttrLiteralIn.d.mts.map +1 -1
- package/dist/rules/AttrLiteralIn.mjs +14 -6
- package/dist/rules/AttrLiteralNotEqual.d.mts +15 -3
- package/dist/rules/AttrLiteralNotEqual.d.mts.map +1 -1
- package/dist/rules/AttrLiteralNotEqual.mjs +26 -12
- package/dist/rules/AttrLiteralNotIn.d.mts +14 -3
- package/dist/rules/AttrLiteralNotIn.d.mts.map +1 -1
- package/dist/rules/AttrLiteralNotIn.mjs +21 -8
- package/dist/rules/AttrMatchRule.d.mts +2 -1
- package/dist/rules/AttrMatchRule.d.mts.map +1 -1
- package/dist/rules/AttrMatchRule.mjs +6 -1
- package/dist/rules/AttrPairCompare.d.mts +11 -1
- package/dist/rules/AttrPairCompare.d.mts.map +1 -1
- package/dist/rules/AttrPairCompare.mjs +22 -10
- package/dist/rules/AttrPairEqual.d.mts +10 -1
- package/dist/rules/AttrPairEqual.d.mts.map +1 -1
- package/dist/rules/AttrPairEqual.mjs +18 -8
- package/dist/rules/AttrPairNotEqual.d.mts +10 -1
- package/dist/rules/AttrPairNotEqual.d.mts.map +1 -1
- package/dist/rules/AttrPairNotEqual.mjs +18 -8
- package/dist/rules/HasPermission.d.mts.map +1 -1
- package/dist/rules/HasScope.d.mts.map +1 -1
- package/dist/rules/collectors/ResourceActionPermissionRuleCollector.d.mts +1 -1
- package/dist/rules/collectors/ResourceActionPermissionRuleCollector.d.mts.map +1 -1
- package/dist/rules/collectors/ResourceActionPermissionRuleCollector.mjs +1 -1
- package/dist/rules/collectors/ResourceActionScopeRuleCollector.d.mts +5 -1
- package/dist/rules/collectors/ResourceActionScopeRuleCollector.d.mts.map +1 -1
- package/package.json +5 -5
package/README.ja.md
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
# @o3co/auth.policy-verifier.builtins
|
|
2
2
|
|
|
3
|
+
最終更新: 2026-09-24
|
|
4
|
+
|
|
3
5
|
auth.policy-verifier 向けの組み込み attribute collector、rule collector、および resource parser です。
|
|
4
6
|
|
|
5
7
|
**Runtime:** `BigInt` と `Map.groupBy` をサポートするサーバー/エッジ JavaScript ランタイムが対象です — Node.js 22+(`engines.node` で宣言しており、古い Node ではインストール時にブロックされます)、Cloudflare Workers、Vercel Edge、Deno、Bun。ブラウザは設計上対象外です(認可判定はサーバー側で enforcement する必要があるため)。同梱の `server` パッケージは引き続き Node 専用です。
|
|
6
8
|
|
|
9
|
+
## 責務と役割
|
|
10
|
+
|
|
11
|
+
core のインターフェースを実装した、小さな既製のセットです: attribute collector、rule、rule collector、resource parser、そしてそれらを server の `createApp` などのホストに登録する `builtinCollectorsModule`。依存は `@o3co/auth.policy-verifier.core` だけです。`templates/standalone` がこれを組み込み、`server` はテストでのみ使います(`devDependencies`)。
|
|
12
|
+
|
|
13
|
+
- **所有するもの:** これらの実装が何を読み、何を書き、どう照合するか — JWT クレーム(`sub`、`azp`、`scope`、宣言したクレーム)から属性キーへの対応(#170)、`HasScope` / `HasPermission` と比較ルールの照合規則、ドット記法のリソース文法。
|
|
14
|
+
- **所有しないもの:** パイプライン、グルーピング、判定(core)、認証や HTTP(`server`)、ポリシーエンジンや `AsyncRule`(`cedar`)、I/O を行う collector(利用側が書くもの。[docs/extending.ja.md](../../docs/extending.ja.md))。
|
|
15
|
+
- **core と別パッケージである理由:** core はエンジン中立の契約で、subject のフィールドを一切名指ししません(#170)。クレームの語彙と具体的な照合はその線の反対側、つまりここに置きます([AGENTS.md — Core Vocabulary Scope](../../AGENTS.md#core-vocabulary-scope))。また任意導入でもあります: builtins は意図的に基本セットであってカタログではない([docs/extending.ja.md](../../docs/extending.ja.md))ので、collector と rule を自前で書くデプロイメントはインストール不要で、core もこれに合わせて肥大化しません。
|
|
16
|
+
|
|
17
|
+
各ソースディレクトリの責務・役割・不変条件(英語):
|
|
18
|
+
[`src/collectors/README.md`](src/collectors/README.md)(attribute collector)、
|
|
19
|
+
[`src/rules/README.md`](src/rules/README.md)(rule と rule collector)。
|
|
20
|
+
|
|
7
21
|
## インストール
|
|
8
22
|
|
|
9
23
|
```bash
|
|
@@ -23,7 +37,7 @@ npm install @o3co/auth.policy-verifier.builtins
|
|
|
23
37
|
| `RequestContextAttributeCollector` | `requestContext` の宣言済みフィールド | 運用者が決めたキー | `{ attributes: Mapping[] }` |
|
|
24
38
|
| `PayloadClaimAttributeCollector` | 検証済み `subject` の宣言済みクレーム | 運用者が決めたキー、または core の 5 キー | `{ attributes: Mapping[] }` (#219) |
|
|
25
39
|
|
|
26
|
-
`StaticPermissionCollector` と `StaticRoleCollector`
|
|
40
|
+
`StaticPermissionCollector` と `StaticRoleCollector` は、リクエストのコンテキストに関わらず、コンストラクタに渡した値を常に出力します。渡された値は構築時にコピーされます — 配列、そして `StaticRoleCollector` では各 `Role` とその `permissions` も — ので、構築後に config を変更しても出力は変わりません(#255)。`permissions` と `roles` は配列でなければなりません。それ以外の値は、文字列も含めて(`["posts.*"]` のつもりで `permissions = "posts.*"` と書いた場合など)構築時に `TypeError` で拒否され、デプロイは起動時に失敗します(#264)。
|
|
27
41
|
|
|
28
42
|
### PayloadClaimAttributeCollector
|
|
29
43
|
|
|
@@ -40,7 +54,7 @@ npm install @o3co/auth.policy-verifier.builtins
|
|
|
40
54
|
|
|
41
55
|
マッピングの形は `RequestContextAttributeCollector` と同じです(`{ from, to?, type? }`、完全一致するキーが dot path より優先、own property のみ)。違うのは読み取り元であり、したがって信頼度です: subject バッグは authenticator が検証したものなので、request-context コレクターが拒否する core の 5 キー — `scopes`、`permissions`、`roles`、`userId`、`clientId` — にもマッピングを着地させて**かまいません**。2 つのコレクターが 1 つのリストキーに書けば union されます。それはデプロイが issuer 由来の 2 つのソースを合成しているということであり、config にそう書かれています。一方、2 つのコレクターが異なる値で書いた*スカラー*キーは `AttributeConflictError` を投げ、すべてのリクエストを deny します — `PayloadSubjectIdCollector` も `userId` / `clientId` を書いている間は、そこへマッピングしないでください。別のパッケージが予約したキー(cedar の `request*`)は引き続き拒否されます。それらは subject ではなくリクエストから導出されるものだからです。そして、マッピングするのは IdP が自身の登録データや管理データから埋めるクレームだけにしてください: ユーザーが編集できるメタデータ(Clerk の `unsafe_metadata`、Auth0 の `user_metadata`)から発行されたクレームは、署名されてはいても信頼できるものではありません。
|
|
42
56
|
|
|
43
|
-
scope クレームについては `PayloadScopeCollector { claim = "scp" }` を優先してください。こちらはスペース区切り文字列の形も読み、`ResourceActionScopeRuleCollector { claim = "scp" }`
|
|
57
|
+
scope クレームについては `PayloadScopeCollector { claim = "scp" }` を優先してください。こちらはスペース区切り文字列の形も読み、`ResourceActionScopeRuleCollector { claim = "scp" }` と組にすることで、両者が同じ claim を見ます(それぞれが自分の `claim` を持ち、一致は検査されないので両方に設定してください)。rule collector は claim の有無だけを見ます — 使える scope リストを持たない claim(`""`、数値)は scope を生まない一方、`scopeless = "skip"` では scope ありとして扱われます。
|
|
44
58
|
|
|
45
59
|
### RequestContextAttributeCollector
|
|
46
60
|
|
|
@@ -116,7 +130,7 @@ new HasScope(scope: string, options?: { allowBareScopeRewrite?: boolean })
|
|
|
116
130
|
|
|
117
131
|
### AttrMatchRule
|
|
118
132
|
|
|
119
|
-
**非推奨。** 代わりに [`AttrPairEqual`](#attrpairequal) を使用してください。`AttrMatchRule` は `AttrPairEqual` を継承した薄いラッパークラスとして残されており、後方互換のため旧来の `ruleType`(`attr_match:${a}:${b}`)と旧来の `message` 文言を保持します。型 `AttrMatchRuleConfig` は `AttrPairEqualConfig`
|
|
133
|
+
**非推奨。** 代わりに [`AttrPairEqual`](#attrpairequal) を使用してください。`AttrMatchRule` は `AttrPairEqual` を継承した薄いラッパークラスとして残されており、後方互換のため旧来の `ruleType`(`attr_match:${a}:${b}`)と旧来の `message` 文言を保持します。型 `AttrMatchRuleConfig` は `AttrPairEqualConfig` の型エイリアスです。v0.3 から非推奨で、将来のリリースで削除される可能性があります(変更の可能性あり)。
|
|
120
134
|
|
|
121
135
|
```ts
|
|
122
136
|
new AttrMatchRule({ a: string, b: string, group?: string })
|
|
@@ -158,7 +172,7 @@ new AttrLiteralNotEqual({ a: string, v: string | number | boolean, group?: strin
|
|
|
158
172
|
|
|
159
173
|
- `code`: `"attr_equal"`。
|
|
160
174
|
- 既定の `ruleType`: `` `attr_literal_not_equal:${a}:${typeof v}:${String(v)}` ``。`typeof v` セグメントは異型リテラル間の衝突を防ぎます(`AttrLiteralEqual` と同じ理由)。
|
|
161
|
-
- `attrs.get(a)` が `v`
|
|
175
|
+
- `attrs.get(a)` が `v` と同じ型で厳密等価でないときに通過します。欠落・型不一致・`NaN` は `false`(safe-deny)を返します。`NaN` はどの数値とも等しくないため、数値でない値で制限が通過しないよう明示的に拒否します(#254)。
|
|
162
176
|
|
|
163
177
|
### AttrLiteralIn
|
|
164
178
|
|
|
@@ -178,7 +192,7 @@ new AttrLiteralNotIn({ a: string, values: (string | number | boolean)[], group?:
|
|
|
178
192
|
|
|
179
193
|
- `code`: `"attr_in_set"`。
|
|
180
194
|
- 既定の `ruleType`: `` `attr_literal_not_in:${a}:${type}:${count}:${hashPrefix}` `` — `AttrLiteralIn` と同じ、重複除去済みの安定ハッシュ方式。
|
|
181
|
-
- `values` は非空かつ同種の配列。`attrs.get(a)`
|
|
195
|
+
- `values` は非空かつ同種の配列。`attrs.get(a)` が `values` と同じ型で、集合に含まれないときに通過します。欠落・型不一致・`NaN` は `false`(safe-deny)を返します — `NaN` はどの集合にも含まれませんが、同様に拒否します(#254)。`values` 内の重複要素は Rule の挙動に影響しません。
|
|
182
196
|
|
|
183
197
|
### AttrLiteralCompare
|
|
184
198
|
|
|
@@ -224,6 +238,10 @@ new AttrPairCompare({ a: string, op: "lt" | "le" | "gt" | "ge", b: string, group
|
|
|
224
238
|
|
|
225
239
|
属性比較 Rule はすべて、上記 `AttrMatchRule` と同じグルーピング規則に従います。既定では各 Rule の `ruleType` は識別用パラメータから導出されるため、異なる要件は評価器によって AND 結合されます。2 つの Rule に同じ `group` 文字列を渡すと、両者は同じ `ruleType` を持つようになり、評価器は OR 結合します(どちらか一方が成立すれば要件を満たす)。
|
|
226
240
|
|
|
241
|
+
### config は構築時にコピーされる
|
|
242
|
+
|
|
243
|
+
属性比較 Rule はすべて、config の各フィールドを一度だけ読んで検証し、自身のコピーとして保持します(#255)。構築後に config オブジェクトを変更しても — `a`、`b`、`op`、`v` の置き換えや `values` の編集 — Rule の応答も `ruleType` と `message` も変わらず、コンストラクタが拒否する値(`NaN` リテラルなど)を後から差し込むこともできません。
|
|
244
|
+
|
|
227
245
|
## Rule Collectors
|
|
228
246
|
|
|
229
247
|
| 名前 | 導出する permission / scope | 返り値 |
|
|
@@ -299,6 +317,8 @@ tchar = %x21 / %x23-2D / %x2F-39 / %x3B-5B / %x5D-7E
|
|
|
299
317
|
import { builtinCollectorsModule } from "@o3co/auth.policy-verifier.builtins";
|
|
300
318
|
```
|
|
301
319
|
|
|
320
|
+
以下は執筆時点の登録内容です。正は [`src/module.mts`](src/module.mts) です。
|
|
321
|
+
|
|
302
322
|
| レジストリ | 名前 | ファクトリ |
|
|
303
323
|
| --- | --- | --- |
|
|
304
324
|
| `attributeCollector` | `"PayloadScopeCollector"` | `(config) => new PayloadScopeCollector(config)` |
|
|
@@ -313,6 +333,7 @@ import { builtinCollectorsModule } from "@o3co/auth.policy-verifier.builtins";
|
|
|
313
333
|
|
|
314
334
|
## 関連
|
|
315
335
|
|
|
336
|
+
- [`src/collectors/README.md`](src/collectors/README.md)、[`src/rules/README.md`](src/rules/README.md) — このパッケージのソースディレクトリの責務・役割・不変条件(英語)
|
|
316
337
|
- [拡張ガイド (`docs/extending.ja.md`)](../../docs/extending.ja.md) — カスタム `Rule` / `AttributeCollector` の書き方と、`builtins` が基本セットとして位置づけられている理由
|
|
317
338
|
- [`@o3co/auth.policy-verifier.core`](../core/README.ja.md) — コアインターフェースと attribute 定数
|
|
318
339
|
- [auth.policy-verifier ルート README](../../README.ja.md) — 完全なセットアップと設定のリファレンス
|
package/README.md
CHANGED
|
@@ -1,9 +1,38 @@
|
|
|
1
1
|
# @o3co/auth.policy-verifier.builtins
|
|
2
2
|
|
|
3
|
+
Last updated: 2026-09-24
|
|
4
|
+
|
|
3
5
|
Built-in attribute collectors, rule collectors, and resource parser for auth.policy-verifier.
|
|
4
6
|
|
|
5
7
|
**Runtime:** Server- and edge-side JavaScript runtimes that support `BigInt` and `Map.groupBy` — Node.js 22+ (declared via `engines.node` so older Node installs are blocked at install time), Cloudflare Workers, Vercel Edge, Deno, Bun. Browsers are out of scope by design: authorization decisions must be enforced server-side. The `server` companion package remains Node-only.
|
|
6
8
|
|
|
9
|
+
## Responsibility
|
|
10
|
+
|
|
11
|
+
A small, ready-made set of implementations of core's interfaces: attribute collectors, rules,
|
|
12
|
+
rule collectors, a resource parser, and `builtinCollectorsModule`, which registers them for
|
|
13
|
+
a host such as the server's `createApp`. It depends on `@o3co/auth.policy-verifier.core` alone;
|
|
14
|
+
`templates/standalone` wires it in, and `server` uses it only in its tests
|
|
15
|
+
(`devDependencies`).
|
|
16
|
+
|
|
17
|
+
- **Owns** what these implementations read, write and match: the mapping from JWT claims
|
|
18
|
+
(`sub`, `azp`, `scope`, declared claims) to attribute keys (#170), the matching rules of
|
|
19
|
+
`HasScope` / `HasPermission` and the comparison rules, and the dot-notation resource
|
|
20
|
+
grammar.
|
|
21
|
+
- **Does not own** the pipelines, the grouping or the decision (core), authentication or HTTP
|
|
22
|
+
(`server`), policy engines or `AsyncRule`s (`cedar`), or collectors that do I/O — those are
|
|
23
|
+
the consumer's to write ([docs/extending.md](../../docs/extending.md)).
|
|
24
|
+
- **Why a separate package from core:** core is the engine-neutral contract and names no
|
|
25
|
+
field of the subject (#170); the claim vocabulary and the concrete matching live here, on
|
|
26
|
+
the other side of that line ([AGENTS.md — Core Vocabulary Scope](../../AGENTS.md#core-vocabulary-scope)).
|
|
27
|
+
It is also optional: builtins is a deliberately basic set, not a catalog
|
|
28
|
+
([docs/extending.md — Positioning](../../docs/extending.md#positioning-builtins-is-a-basic-set-not-an-exhaustive-catalog)),
|
|
29
|
+
so a deployment that writes its own collectors and rules does not have to install it, and
|
|
30
|
+
core does not grow with it.
|
|
31
|
+
|
|
32
|
+
Responsibility, role and invariants of each source directory:
|
|
33
|
+
[`src/collectors/README.md`](src/collectors/README.md) (attribute collectors) and
|
|
34
|
+
[`src/rules/README.md`](src/rules/README.md) (rules and rule collectors).
|
|
35
|
+
|
|
7
36
|
## Install
|
|
8
37
|
|
|
9
38
|
```bash
|
|
@@ -23,7 +52,7 @@ All collectors implement `AttributeCollector`.
|
|
|
23
52
|
| `RequestContextAttributeCollector` | declared fields of `requestContext` | the operator's own keys | `{ attributes: Mapping[] }` |
|
|
24
53
|
| `PayloadClaimAttributeCollector` | declared claims of the verified `subject` | the operator's own keys, or core's five | `{ attributes: Mapping[] }` (#219) |
|
|
25
54
|
|
|
26
|
-
`StaticPermissionCollector` and `StaticRoleCollector` always emit the values supplied at construction time, regardless of request context.
|
|
55
|
+
`StaticPermissionCollector` and `StaticRoleCollector` always emit the values supplied at construction time, regardless of request context. They copy what they were given at construction — the array, and for `StaticRoleCollector` each `Role` and its `permissions` — so mutating the config afterwards changes nothing they emit (#255). `permissions` and `roles` must be arrays: anything else, a string included (`permissions = "posts.*"` where `["posts.*"]` was meant), is refused with a `TypeError` at construction, so the deployment fails at boot (#264).
|
|
27
56
|
|
|
28
57
|
### PayloadClaimAttributeCollector
|
|
29
58
|
|
|
@@ -40,7 +69,7 @@ Promotes declared claims of the **verified subject** into attributes (#219) —
|
|
|
40
69
|
|
|
41
70
|
Same mapping shape as `RequestContextAttributeCollector` (`{ from, to?, type? }`, an exact key winning over a dot path, own properties only). What differs is the source, and therefore the trust: the subject bag is what the authenticator verified, so a mapping **may** land on core's five keys — `scopes`, `permissions`, `roles`, `userId`, `clientId` — where the request-context collector refuses them. Two collectors writing one list key union it; that is the deployment composing two issuer-derived sources, and it says so in config. A *scalar* key written by two collectors with different values throws `AttributeConflictError` and denies every request — do not map onto `userId` / `clientId` while `PayloadSubjectIdCollector` also writes them. Keys another package reserved (cedar's `request*`) stay refused, because they are derived from the request, not from the subject. And map only claims the IdP populates from its own registration or admin data: a claim minted from user-editable metadata (Clerk's `unsafe_metadata`, Auth0's `user_metadata`) is signed, not trusted.
|
|
42
71
|
|
|
43
|
-
For the scope claim specifically, prefer `PayloadScopeCollector { claim = "scp" }`, which also reads the space-delimited string form and pairs with `ResourceActionScopeRuleCollector { claim = "scp" }` so both
|
|
72
|
+
For the scope claim specifically, prefer `PayloadScopeCollector { claim = "scp" }`, which also reads the space-delimited string form and pairs with `ResourceActionScopeRuleCollector { claim = "scp" }` — set on both, since each keeps its own `claim` and nothing checks they match — so both look at the same claim. The rule collector checks only that the claim is present: a claim holding no usable scope list (`""`, a number) yields no scopes but still counts as scoped under `scopeless = "skip"`.
|
|
44
73
|
|
|
45
74
|
### RequestContextAttributeCollector
|
|
46
75
|
|
|
@@ -116,7 +145,7 @@ new HasScope(scope: string, options?: { allowBareScopeRewrite?: boolean })
|
|
|
116
145
|
|
|
117
146
|
### AttrMatchRule
|
|
118
147
|
|
|
119
|
-
**Deprecated.** Use [`AttrPairEqual`](#attrpairequal) instead. `AttrMatchRule` is kept as a thin wrapper class that extends `AttrPairEqual` and preserves the legacy `ruleType` (`attr_match:${a}:${b}`) and legacy `message` wording for backward compatibility. The type `AttrMatchRuleConfig` is a type alias of `AttrPairEqualConfig`.
|
|
148
|
+
**Deprecated.** Use [`AttrPairEqual`](#attrpairequal) instead. `AttrMatchRule` is kept as a thin wrapper class that extends `AttrPairEqual` and preserves the legacy `ruleType` (`attr_match:${a}:${b}`) and legacy `message` wording for backward compatibility. The type `AttrMatchRuleConfig` is a type alias of `AttrPairEqualConfig`. Deprecated since v0.3; it may be removed in a future release (subject to change).
|
|
120
149
|
|
|
121
150
|
```ts
|
|
122
151
|
new AttrMatchRule({ a: string, b: string, group?: string })
|
|
@@ -158,7 +187,7 @@ new AttrLiteralNotEqual({ a: string, v: string | number | boolean, group?: strin
|
|
|
158
187
|
|
|
159
188
|
- `code`: `"attr_equal"`.
|
|
160
189
|
- Default `ruleType`: `` `attr_literal_not_equal:${a}:${typeof v}:${String(v)}` ``. The `typeof v` segment prevents silent collisions between distinct-type literals (same rationale as `AttrLiteralEqual`).
|
|
161
|
-
- Passes when `attrs.get(a)` is the same type as `v` and strictly not equal to it. Missing
|
|
190
|
+
- Passes when `attrs.get(a)` is the same type as `v` and strictly not equal to it. Missing, wrong-type or `NaN` attributes return `false` (safe-deny): `NaN` is unequal to every number, and a restriction must not pass on a value that is not a number at all (#254).
|
|
162
191
|
|
|
163
192
|
### AttrLiteralIn
|
|
164
193
|
|
|
@@ -178,7 +207,7 @@ new AttrLiteralNotIn({ a: string, values: (string | number | boolean)[], group?:
|
|
|
178
207
|
|
|
179
208
|
- `code`: `"attr_in_set"`.
|
|
180
209
|
- Default `ruleType`: `` `attr_literal_not_in:${a}:${type}:${count}:${hashPrefix}` `` — same stable, deduplication-aware hash scheme as `AttrLiteralIn`.
|
|
181
|
-
- `values` must be a non-empty, homogeneous array. Passes when `attrs.get(a)` is NOT in the set. Duplicate elements in `values` are ignored.
|
|
210
|
+
- `values` must be a non-empty, homogeneous array. Passes when `attrs.get(a)` is the same type as the values and NOT in the set. Missing, wrong-type or `NaN` attributes return `false` (safe-deny); `NaN` is in no set, so it would otherwise pass (#254). Duplicate elements in `values` are ignored.
|
|
182
211
|
|
|
183
212
|
### AttrLiteralCompare
|
|
184
213
|
|
|
@@ -224,6 +253,10 @@ new AttrPairCompare({ a: string, op: "lt" | "le" | "gt" | "ge", b: string, group
|
|
|
224
253
|
|
|
225
254
|
All attribute comparison rules follow the same grouping semantics described for `AttrMatchRule` above. By default, each rule's `ruleType` is derived from its distinguishing parameters so that distinct requirements are AND-combined by the evaluator. Pass the same `group` string to two rules to give them the same `ruleType` — the evaluator then OR-combines them (either condition satisfies the requirement).
|
|
226
255
|
|
|
256
|
+
### Configuration is copied at construction
|
|
257
|
+
|
|
258
|
+
Every attribute comparison rule reads each field of its config once, validates it, and keeps its own copy (#255). Mutating the config object afterwards — replacing `a`, `b`, `op` or `v`, or editing `values` — changes neither the rule's answers nor its `ruleType` and `message`, and a value the constructor would refuse (such as a `NaN` literal) cannot be installed after it.
|
|
259
|
+
|
|
227
260
|
## Rule Collectors
|
|
228
261
|
|
|
229
262
|
| Name | Derives permission/scope | Returns |
|
|
@@ -301,6 +334,8 @@ that syntax.
|
|
|
301
334
|
import { builtinCollectorsModule } from "@o3co/auth.policy-verifier.builtins";
|
|
302
335
|
```
|
|
303
336
|
|
|
337
|
+
Registrations as of this writing; the source of truth is [`src/module.mts`](src/module.mts).
|
|
338
|
+
|
|
304
339
|
| Registry | Name | Factory |
|
|
305
340
|
| --- | --- | --- |
|
|
306
341
|
| `attributeCollector` | `"PayloadScopeCollector"` | `(config) => new PayloadScopeCollector(config)` |
|
|
@@ -315,6 +350,7 @@ import { builtinCollectorsModule } from "@o3co/auth.policy-verifier.builtins";
|
|
|
315
350
|
|
|
316
351
|
## See Also
|
|
317
352
|
|
|
353
|
+
- [`src/collectors/README.md`](src/collectors/README.md), [`src/rules/README.md`](src/rules/README.md) — responsibility, role and invariants of this package's source directories
|
|
318
354
|
- [Extension guide (`docs/extending.md`)](../../docs/extending.md) — how to write custom `Rule` and `AttributeCollector` implementations; positioning of `builtins` as a basic set
|
|
319
355
|
- [`@o3co/auth.policy-verifier.core`](../core/README.md) — core interfaces and attribute constants
|
|
320
356
|
- [auth.policy-verifier root README](../../README.md) — full setup and configuration reference
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PayloadClaimAttributeCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/PayloadClaimAttributeCollector.mts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PayloadClaimAttributeCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/PayloadClaimAttributeCollector.mts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACX,kBAAkB,EAElB,UAAU,EACV,gBAAgB,EAChB,MAAM,iCAAiC,CAAC;AAKzC,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,+BAA+B,EACpC,KAAK,oBAAoB,EAIzB,MAAM,yBAAyB,CAAC;AAEjC,yEAAyE;AACzE,MAAM,MAAM,4BAA4B,GAAG,gBAAgB,CAAC;AAC5D,wCAAwC;AACxC,MAAM,MAAM,yBAAyB,GAAG,oBAAoB,CAAC;AAC7D,iEAAiE;AACjE,MAAM,MAAM,oCAAoC,GAAG,+BAA+B,CAAC;AAEnF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,qBAAa,8BAA+B,YAAW,kBAAkB;IACxE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;gBAE1C,MAAM,EAAE,oCAAoC;IASlD,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAK7D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PayloadScopeCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/PayloadScopeCollector.mts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PayloadScopeCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/PayloadScopeCollector.mts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACX,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,MAAM,iCAAiC,CAAC;AAIzC,wDAAwD;AACxD,MAAM,WAAW,2BAA2B;IAC3C;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,qBAAa,qBAAsB,YAAW,kBAAkB;IAC/D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,MAAM,CAAC,EAAE,2BAA2B;IAI1C,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAG7D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PayloadSubjectIdCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/PayloadSubjectIdCollector.mts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PayloadSubjectIdCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/PayloadSubjectIdCollector.mts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACX,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,MAAM,iCAAiC,CAAC;AAGzC;;;;;;;;GAQG;AACH,qBAAa,yBAA0B,YAAW,kBAAkB;IAC7D,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAW7D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RequestContextAttributeCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/RequestContextAttributeCollector.mts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"RequestContextAttributeCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/RequestContextAttributeCollector.mts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACX,kBAAkB,EAElB,UAAU,EACV,gBAAgB,EAChB,MAAM,iCAAiC,CAAC;AAMzC,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,+BAA+B,EACpC,KAAK,oBAAoB,EAIzB,MAAM,yBAAyB,CAAC;AAEjC,yDAAyD;AACzD,MAAM,MAAM,2BAA2B,GAAG,oBAAoB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,MAAM,8BAA8B,GAAG,gBAAgB,CAAC;AAE9D,mEAAmE;AACnE,MAAM,MAAM,sCAAsC,GAAG,+BAA+B,CAAC;AAErF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AACH,qBAAa,gCAAiC,YAAW,kBAAkB;IAC1E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;gBAE1C,MAAM,EAAE,sCAAsC;IAOpD,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAQ7D"}
|
|
@@ -3,9 +3,23 @@ import type { AttributeCollector, Attributes, CollectorContext } from "@o3co/aut
|
|
|
3
3
|
* Attribute collector that returns a configured constant permission list under
|
|
4
4
|
* `ATTR_PERMISSIONS`, independent of the JWT payload. Useful for
|
|
5
5
|
* environments where permissions are static per deployment.
|
|
6
|
+
*
|
|
7
|
+
* The list is copied at construction (#255): a caller that mutates the config
|
|
8
|
+
* or its array afterwards changes nothing this collector emits.
|
|
9
|
+
*
|
|
10
|
+
* Only an array is accepted (#264). Anything else — a missing field, `null`, a
|
|
11
|
+
* number, an object, a string — is refused with a `TypeError` naming the
|
|
12
|
+
* collector and the field, so a misconfigured deployment fails at boot. A
|
|
13
|
+
* string is the case that matters: it is iterable, so copying it would split
|
|
14
|
+
* `"posts.*"` into single characters, and `HasPermission` honours a lone `*`
|
|
15
|
+
* as grant-all.
|
|
16
|
+
*
|
|
17
|
+
* The entries are not checked, and a non-string one is copied as it is. It is
|
|
18
|
+
* inert: `HasPermission` skips every permission that is not a string, so such
|
|
19
|
+
* an entry matches no requirement and grants nothing.
|
|
6
20
|
*/
|
|
7
21
|
export declare class StaticPermissionCollector implements AttributeCollector {
|
|
8
|
-
private permissions;
|
|
22
|
+
private readonly permissions;
|
|
9
23
|
constructor(config: {
|
|
10
24
|
permissions: string[];
|
|
11
25
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StaticPermissionCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/StaticPermissionCollector.mts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"StaticPermissionCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/StaticPermissionCollector.mts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACX,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,MAAM,iCAAiC,CAAC;AAGzC;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,yBAA0B,YAAW,kBAAkB;IACnE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;gBAEpC,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,EAAE,CAAA;KAAE;IAUvC,OAAO,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAG9D"}
|
|
@@ -5,11 +5,29 @@ import { ATTR_PERMISSIONS } from "@o3co/auth.policy-verifier.core";
|
|
|
5
5
|
* Attribute collector that returns a configured constant permission list under
|
|
6
6
|
* `ATTR_PERMISSIONS`, independent of the JWT payload. Useful for
|
|
7
7
|
* environments where permissions are static per deployment.
|
|
8
|
+
*
|
|
9
|
+
* The list is copied at construction (#255): a caller that mutates the config
|
|
10
|
+
* or its array afterwards changes nothing this collector emits.
|
|
11
|
+
*
|
|
12
|
+
* Only an array is accepted (#264). Anything else — a missing field, `null`, a
|
|
13
|
+
* number, an object, a string — is refused with a `TypeError` naming the
|
|
14
|
+
* collector and the field, so a misconfigured deployment fails at boot. A
|
|
15
|
+
* string is the case that matters: it is iterable, so copying it would split
|
|
16
|
+
* `"posts.*"` into single characters, and `HasPermission` honours a lone `*`
|
|
17
|
+
* as grant-all.
|
|
18
|
+
*
|
|
19
|
+
* The entries are not checked, and a non-string one is copied as it is. It is
|
|
20
|
+
* inert: `HasPermission` skips every permission that is not a string, so such
|
|
21
|
+
* an entry matches no requirement and grants nothing.
|
|
8
22
|
*/
|
|
9
23
|
export class StaticPermissionCollector {
|
|
10
24
|
permissions;
|
|
11
25
|
constructor(config) {
|
|
12
|
-
|
|
26
|
+
const { permissions } = config;
|
|
27
|
+
if (!Array.isArray(permissions)) {
|
|
28
|
+
throw new TypeError(`StaticPermissionCollector: permissions must be an array (got ${permissions === null ? "null" : typeof permissions})`);
|
|
29
|
+
}
|
|
30
|
+
this.permissions = [...permissions];
|
|
13
31
|
}
|
|
14
32
|
async collect(_context) {
|
|
15
33
|
return new Map([[ATTR_PERMISSIONS, [...this.permissions]]]);
|
|
@@ -3,9 +3,25 @@ import type { AttributeCollector, Attributes, CollectorContext, Role } from "@o3
|
|
|
3
3
|
* Attribute collector that returns a configured constant role list under
|
|
4
4
|
* `ATTR_ROLES`, independent of the JWT payload. Each role bundles a name and
|
|
5
5
|
* the permissions it implies.
|
|
6
|
+
*
|
|
7
|
+
* The list is copied at construction, and so is each role in it and each
|
|
8
|
+
* role's `permissions` array (#255): a caller that mutates the config, its
|
|
9
|
+
* array or a `Role` in it afterwards changes nothing this collector emits.
|
|
10
|
+
*
|
|
11
|
+
* Only an array is accepted (#264). Anything else — a missing field, `null`, a
|
|
12
|
+
* number, an object, a string — is refused with a `TypeError` naming the
|
|
13
|
+
* collector and the field, so a misconfigured deployment fails at boot. A
|
|
14
|
+
* string is iterable and would otherwise be split into single characters, as
|
|
15
|
+
* it would be in `StaticPermissionCollector`.
|
|
16
|
+
*
|
|
17
|
+
* The entries are not checked: a malformed `Role` entry is copied as it is
|
|
18
|
+
* (see `copyRole`). It is inert: `HasPermission` ignores a role that is not an
|
|
19
|
+
* object or whose `permissions` is not an array, and a non-string permission
|
|
20
|
+
* inside a role. Each collect returns a shallow copy of the list, so its `Role`
|
|
21
|
+
* copies are shared between the outputs of different collects.
|
|
6
22
|
*/
|
|
7
23
|
export declare class StaticRoleCollector implements AttributeCollector {
|
|
8
|
-
private roles;
|
|
24
|
+
private readonly roles;
|
|
9
25
|
constructor(config: {
|
|
10
26
|
roles: Role[];
|
|
11
27
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StaticRoleCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/StaticRoleCollector.mts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"StaticRoleCollector.d.mts","sourceRoot":"","sources":["../../src/collectors/StaticRoleCollector.mts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACX,kBAAkB,EAClB,UAAU,EACV,gBAAgB,EAChB,IAAI,EACJ,MAAM,iCAAiC,CAAC;AAGzC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,mBAAoB,YAAW,kBAAkB;IAC7D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;gBAE5B,MAAM,EAAE;QAAE,KAAK,EAAE,IAAI,EAAE,CAAA;KAAE;IAU/B,OAAO,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAG9D"}
|
|
@@ -5,13 +5,52 @@ import { ATTR_ROLES } from "@o3co/auth.policy-verifier.core";
|
|
|
5
5
|
* Attribute collector that returns a configured constant role list under
|
|
6
6
|
* `ATTR_ROLES`, independent of the JWT payload. Each role bundles a name and
|
|
7
7
|
* the permissions it implies.
|
|
8
|
+
*
|
|
9
|
+
* The list is copied at construction, and so is each role in it and each
|
|
10
|
+
* role's `permissions` array (#255): a caller that mutates the config, its
|
|
11
|
+
* array or a `Role` in it afterwards changes nothing this collector emits.
|
|
12
|
+
*
|
|
13
|
+
* Only an array is accepted (#264). Anything else — a missing field, `null`, a
|
|
14
|
+
* number, an object, a string — is refused with a `TypeError` naming the
|
|
15
|
+
* collector and the field, so a misconfigured deployment fails at boot. A
|
|
16
|
+
* string is iterable and would otherwise be split into single characters, as
|
|
17
|
+
* it would be in `StaticPermissionCollector`.
|
|
18
|
+
*
|
|
19
|
+
* The entries are not checked: a malformed `Role` entry is copied as it is
|
|
20
|
+
* (see `copyRole`). It is inert: `HasPermission` ignores a role that is not an
|
|
21
|
+
* object or whose `permissions` is not an array, and a non-string permission
|
|
22
|
+
* inside a role. Each collect returns a shallow copy of the list, so its `Role`
|
|
23
|
+
* copies are shared between the outputs of different collects.
|
|
8
24
|
*/
|
|
9
25
|
export class StaticRoleCollector {
|
|
10
26
|
roles;
|
|
11
27
|
constructor(config) {
|
|
12
|
-
|
|
28
|
+
const { roles } = config;
|
|
29
|
+
if (!Array.isArray(roles)) {
|
|
30
|
+
throw new TypeError(`StaticRoleCollector: roles must be an array (got ${roles === null ? "null" : typeof roles})`);
|
|
31
|
+
}
|
|
32
|
+
this.roles = [...roles].map(copyRole);
|
|
13
33
|
}
|
|
14
34
|
async collect(_context) {
|
|
15
35
|
return new Map([[ATTR_ROLES, [...this.roles]]]);
|
|
16
36
|
}
|
|
17
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Copies one configured role: the object, and its `permissions` array.
|
|
40
|
+
*
|
|
41
|
+
* Nothing about the entry is validated here, and a malformed one keeps its
|
|
42
|
+
* shape rather than being coerced — `HasPermission` ignores a role that is not
|
|
43
|
+
* an object or whose `permissions` is not an array (#180), and spreading a bare
|
|
44
|
+
* string under `permissions` would splay it into characters it then honours.
|
|
45
|
+
*/
|
|
46
|
+
function copyRole(role) {
|
|
47
|
+
if (typeof role !== "object" || role === null)
|
|
48
|
+
return role;
|
|
49
|
+
// An array is an object too: spreading it into `{ ...role }` would turn it
|
|
50
|
+
// into an index-keyed object, so it is copied as an array.
|
|
51
|
+
if (Array.isArray(role))
|
|
52
|
+
return [...role];
|
|
53
|
+
return Array.isArray(role.permissions)
|
|
54
|
+
? { ...role, permissions: [...role.permissions] }
|
|
55
|
+
: { ...role };
|
|
56
|
+
}
|
|
@@ -38,8 +38,16 @@ export declare function parseAttributeMappings(collector: string, config: Attrib
|
|
|
38
38
|
* through a configured name.
|
|
39
39
|
*/
|
|
40
40
|
export declare function readPath(root: Record<string, unknown>, path: string): unknown;
|
|
41
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Whether `value` is usable as the declared type. Empty strings count as
|
|
43
|
+
* absent, so one empty entry disqualifies a whole `string[]`. A `number` must
|
|
44
|
+
* be finite: `NaN` and `±Infinity` are dropped here, while a rule's comparand
|
|
45
|
+
* (`requireNumber` in `../rules/_sharedValidation.mts`) may be `Infinity`.
|
|
46
|
+
*/
|
|
42
47
|
export declare function matchesType(value: unknown, type: AttributeMappingType): boolean;
|
|
43
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Promotes every declared field whose value matches its declared type, into a
|
|
50
|
+
* fresh map; a promoted list is a copy, not the source's array.
|
|
51
|
+
*/
|
|
44
52
|
export declare function promoteMappings(mappings: readonly ResolvedAttributeMapping[], source: Record<string, unknown>): Attributes;
|
|
45
53
|
//# sourceMappingURL=_attributeMapping.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_attributeMapping.d.mts","sourceRoot":"","sources":["../../src/collectors/_attributeMapping.mts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAG3F,wCAAwC;AACxC,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;AAEhF,eAAO,MAAM,uBAAuB,EAAE,SAAS,oBAAoB,EAKlE,CAAC;AAEF,4BAA4B;AAC5B,MAAM,WAAW,gBAAgB;IAChC;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,yFAAyF;IACzF,IAAI,CAAC,EAAE,oBAAoB,CAAC;CAC5B;AAED,2DAA2D;AAC3D,MAAM,WAAW,+BAA+B;IAC/C,wDAAwD;IACxD,UAAU,EAAE,gBAAgB,EAAE,CAAC;CAC/B;AAED,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC;AAElE;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC/B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,uBAAuB,KAChC,MAAM,GAAG,SAAS,CAAC;AAExB;;;GAGG;AACH,wBAAgB,sBAAsB,CACrC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,+BAA+B,GAAG,SAAS,EACnD,iBAAiB,EAAE,iBAAiB,GAClC,wBAAwB,EAAE,CA4B5B;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAS7E;AAED
|
|
1
|
+
{"version":3,"file":"_attributeMapping.d.mts","sourceRoot":"","sources":["../../src/collectors/_attributeMapping.mts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAG3F,wCAAwC;AACxC,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;AAEhF,eAAO,MAAM,uBAAuB,EAAE,SAAS,oBAAoB,EAKlE,CAAC;AAEF,4BAA4B;AAC5B,MAAM,WAAW,gBAAgB;IAChC;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,yFAAyF;IACzF,IAAI,CAAC,EAAE,oBAAoB,CAAC;CAC5B;AAED,2DAA2D;AAC3D,MAAM,WAAW,+BAA+B;IAC/C,wDAAwD;IACxD,UAAU,EAAE,gBAAgB,EAAE,CAAC;CAC/B;AAED,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC;AAElE;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAC/B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,uBAAuB,KAChC,MAAM,GAAG,SAAS,CAAC;AAExB;;;GAGG;AACH,wBAAgB,sBAAsB,CACrC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,+BAA+B,GAAG,SAAS,EACnD,iBAAiB,EAAE,iBAAiB,GAClC,wBAAwB,EAAE,CA4B5B;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAS7E;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAW/E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC9B,QAAQ,EAAE,SAAS,wBAAwB,EAAE,EAC7C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,UAAU,CASZ"}
|
|
@@ -58,7 +58,12 @@ export function readPath(root, path) {
|
|
|
58
58
|
}
|
|
59
59
|
return current;
|
|
60
60
|
}
|
|
61
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* Whether `value` is usable as the declared type. Empty strings count as
|
|
63
|
+
* absent, so one empty entry disqualifies a whole `string[]`. A `number` must
|
|
64
|
+
* be finite: `NaN` and `±Infinity` are dropped here, while a rule's comparand
|
|
65
|
+
* (`requireNumber` in `../rules/_sharedValidation.mts`) may be `Infinity`.
|
|
66
|
+
*/
|
|
62
67
|
export function matchesType(value, type) {
|
|
63
68
|
switch (type) {
|
|
64
69
|
case "string":
|
|
@@ -71,7 +76,10 @@ export function matchesType(value, type) {
|
|
|
71
76
|
return Array.isArray(value) && value.every((item) => typeof item === "string" && item !== "");
|
|
72
77
|
}
|
|
73
78
|
}
|
|
74
|
-
/**
|
|
79
|
+
/**
|
|
80
|
+
* Promotes every declared field whose value matches its declared type, into a
|
|
81
|
+
* fresh map; a promoted list is a copy, not the source's array.
|
|
82
|
+
*/
|
|
75
83
|
export function promoteMappings(mappings, source) {
|
|
76
84
|
const attrs = new Map();
|
|
77
85
|
for (const mapping of mappings) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_claims.d.mts","sourceRoot":"","sources":["../../src/collectors/_claims.mts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"_claims.d.mts","sourceRoot":"","sources":["../../src/collectors/_claims.mts"],"names":[],"mappings":"AAaA,uEAAuE;AACvE,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAE3C;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM1F;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAMnD"}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2026 1o1 Co. Ltd.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
/*
|
|
4
|
-
* Which claim carries the scopes, and how its value is read (#219).
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Which claim carries the scopes, and how its value is read (#219).
|
|
5
|
+
* `PayloadScopeCollector` and `ResourceActionScopeRuleCollector` share the
|
|
6
|
+
* default claim name and refuse the same bad `claim` option; only
|
|
7
|
+
* `PayloadScopeCollector` reads the value (`scopesFrom`), while the rule
|
|
8
|
+
* collector checks only whether the claim is present. Each keeps its own
|
|
9
|
+
* `claim` option, and nothing checks that both were given the same name. Not
|
|
10
|
+
* exported from the package.
|
|
7
11
|
*/
|
|
8
12
|
/** The claim RFC 8693 §4.2 / RFC 9068 §2.2.3 name, and the default. */
|
|
9
13
|
export const DEFAULT_SCOPE_CLAIM = "scope";
|
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":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAW9D;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,MA6CrC,CAAC"}
|
|
@@ -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":"AASA,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"}
|
|
@@ -19,12 +19,22 @@ export interface AttrLiteralCompareConfig {
|
|
|
19
19
|
* `attr_literal_compare:{a}:{op}:{String(v)}`
|
|
20
20
|
*
|
|
21
21
|
* Pass an explicit `group` string to override the default ruleType entirely.
|
|
22
|
+
*
|
|
23
|
+
* ## Configuration is copied at construction
|
|
24
|
+
*
|
|
25
|
+
* The constructor reads each field of `config` once, validates it, and keeps
|
|
26
|
+
* the validated value in a field of its own; the object is not retained (#255).
|
|
27
|
+
* A caller that mutates the config afterwards changes nothing: the rule answers
|
|
28
|
+
* from the values it validated, and `ruleType` and `message` keep describing
|
|
29
|
+
* them.
|
|
22
30
|
*/
|
|
23
31
|
export declare class AttrLiteralCompare implements Rule {
|
|
24
|
-
private readonly config;
|
|
25
32
|
readonly ruleType: string;
|
|
26
33
|
readonly code = "attr_compare_violated";
|
|
27
34
|
readonly message: string;
|
|
35
|
+
private readonly a;
|
|
36
|
+
private readonly op;
|
|
37
|
+
private readonly v;
|
|
28
38
|
constructor(config: AttrLiteralCompareConfig);
|
|
29
39
|
verify(attrs: ReadonlyAttributes): boolean;
|
|
30
40
|
}
|
|
@@ -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":"AAQA,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;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,kBAAmB,YAAW,IAAI;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,2BAA2B;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAY;IAC/B,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAS;gBAEf,MAAM,EAAE,wBAAwB;IAa5C,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO;CAK1C"}
|
|
@@ -14,25 +14,37 @@ import { applyCompare, requireAttrName, requireCompareOp, requireNumber, require
|
|
|
14
14
|
* `attr_literal_compare:{a}:{op}:{String(v)}`
|
|
15
15
|
*
|
|
16
16
|
* Pass an explicit `group` string to override the default ruleType entirely.
|
|
17
|
+
*
|
|
18
|
+
* ## Configuration is copied at construction
|
|
19
|
+
*
|
|
20
|
+
* The constructor reads each field of `config` once, validates it, and keeps
|
|
21
|
+
* the validated value in a field of its own; the object is not retained (#255).
|
|
22
|
+
* A caller that mutates the config afterwards changes nothing: the rule answers
|
|
23
|
+
* from the values it validated, and `ruleType` and `message` keep describing
|
|
24
|
+
* them.
|
|
17
25
|
*/
|
|
18
26
|
export class AttrLiteralCompare {
|
|
19
|
-
config;
|
|
20
27
|
ruleType;
|
|
21
28
|
code = "attr_compare_violated";
|
|
22
29
|
message;
|
|
30
|
+
a;
|
|
31
|
+
op;
|
|
32
|
+
v;
|
|
23
33
|
constructor(config) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
requireCompareOp("AttrLiteralCompare", config.op);
|
|
34
|
+
const a = requireAttrName("AttrLiteralCompare", "a", config.a);
|
|
35
|
+
const v = requireNumber("AttrLiteralCompare", "v", config.v);
|
|
36
|
+
const op = requireCompareOp("AttrLiteralCompare", config.op);
|
|
28
37
|
const group = requireOptionalGroup("AttrLiteralCompare", config.group);
|
|
29
|
-
this.
|
|
30
|
-
this.
|
|
38
|
+
this.a = a;
|
|
39
|
+
this.op = op;
|
|
40
|
+
this.v = v;
|
|
41
|
+
this.ruleType = group ?? `attr_literal_compare:${a}:${op}:${String(v)}`;
|
|
42
|
+
this.message = `Attribute constraint not satisfied: ${a} must be ${op} ${String(v)}.`;
|
|
31
43
|
}
|
|
32
44
|
verify(attrs) {
|
|
33
|
-
const x = attrs.get(this.
|
|
45
|
+
const x = attrs.get(this.a);
|
|
34
46
|
if (typeof x !== "number")
|
|
35
47
|
return false;
|
|
36
|
-
return applyCompare(this.
|
|
48
|
+
return applyCompare(this.op, x, this.v);
|
|
37
49
|
}
|
|
38
50
|
}
|
|
@@ -25,12 +25,21 @@ export interface AttrLiteralEqualConfig {
|
|
|
25
25
|
*
|
|
26
26
|
* Pass a shared `group` string to two instances to opt into OR semantics
|
|
27
27
|
* (e.g. "role is admin" OR "role is superuser" under one group).
|
|
28
|
+
*
|
|
29
|
+
* ## Configuration is copied at construction
|
|
30
|
+
*
|
|
31
|
+
* The constructor reads each field of `config` once, validates it, and keeps
|
|
32
|
+
* the validated value in a field of its own; the object is not retained (#255).
|
|
33
|
+
* A caller that mutates the config afterwards changes nothing: the rule answers
|
|
34
|
+
* from the values it validated, and `ruleType` and `message` keep describing
|
|
35
|
+
* them.
|
|
28
36
|
*/
|
|
29
37
|
export declare class AttrLiteralEqual implements Rule {
|
|
30
|
-
private readonly config;
|
|
31
38
|
readonly ruleType: string;
|
|
32
39
|
readonly code = "attr_not_equal";
|
|
33
40
|
readonly message: string;
|
|
41
|
+
private readonly a;
|
|
42
|
+
private readonly v;
|
|
34
43
|
constructor(config: AttrLiteralEqualConfig);
|
|
35
44
|
verify(attrs: ReadonlyAttributes): boolean;
|
|
36
45
|
}
|
|
@@ -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":"AAQA,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,gBAAiB,YAAW,IAAI;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,oBAAoB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAe;gBAErB,MAAM,EAAE,sBAAsB;IAW1C,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO;CAK1C"}
|