@o3co/auth.policy-verifier.builtins 0.9.0 → 0.11.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 CHANGED
@@ -16,14 +16,32 @@ npm install @o3co/auth.policy-verifier.builtins
16
16
 
17
17
  | 名前 | 読み取り元 | 出力 | コンストラクタ引数 |
18
18
  | --- | --- | --- | --- |
19
- | `PayloadScopeCollector` | `subject.scope`(スペース区切り文字列) | `ATTR_SCOPES: string[]` | なし |
19
+ | `PayloadScopeCollector` | scope クレーム — 既定は `subject.scope`。スペース区切り文字列または文字列の配列 | `ATTR_SCOPES: string[]` | `{ claim?: string }`(#219: Okta なら `"scp"`、Auth0 なら `"permissions"`) |
20
20
  | `PayloadSubjectIdCollector` | `subject.sub`、`subject.azp` | `ATTR_USER_ID`、`ATTR_CLIENT_ID` | なし |
21
21
  | `StaticPermissionCollector` | — | `ATTR_PERMISSIONS: string[]` | `{ permissions: string[] }` |
22
22
  | `StaticRoleCollector` | — | `ATTR_ROLES: Role[]` | `{ roles: Role[] }` |
23
23
  | `RequestContextAttributeCollector` | `requestContext` の宣言済みフィールド | 運用者が決めたキー | `{ attributes: Mapping[] }` |
24
+ | `PayloadClaimAttributeCollector` | 検証済み `subject` の宣言済みクレーム | 運用者が決めたキー、または core の 5 キー | `{ attributes: Mapping[] }` (#219) |
24
25
 
25
26
  `StaticPermissionCollector` と `StaticRoleCollector` は、リクエストのコンテキストに関わらず、コンストラクタに渡した値を常に出力します。
26
27
 
28
+ ### PayloadClaimAttributeCollector
29
+
30
+ **検証済み subject** の宣言済みクレームを attribute に昇格させます (#219) — 専用のコレクターを書かずに、外部 IdP のクレームを Rule が読む形へ変える、運用者が宣言する方法です:
31
+
32
+ ```hocon
33
+ { collector = "PayloadClaimAttributeCollector"
34
+ attributes = [
35
+ { from = "o.rol", to = "roles", type = "string[]" } # Clerk: org のロール。dot path で指定
36
+ { from = "https://example.com/roles", to = "roles", type = "string[]" } # Auth0: 名前空間付きクレームはパスではなく 1 つのキー
37
+ { from = "tid", to = "tenantId" }
38
+ ] }
39
+ ```
40
+
41
+ マッピングの形は `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
+
43
+ scope クレームについては `PayloadScopeCollector { claim = "scp" }` を優先してください。こちらはスペース区切り文字列の形も読み、`ResourceActionScopeRuleCollector { claim = "scp" }` と組にすることで、どのトークンが scopeless かについて両者の判断が一致します。
44
+
27
45
  ### RequestContextAttributeCollector
28
46
 
29
47
  `CollectorContext.requestContext` の宣言済みフィールドを attribute に昇格させます:
@@ -36,7 +54,7 @@ npm install @o3co/auth.policy-verifier.builtins
36
54
  ] }
37
55
  ```
38
56
 
39
- 各マッピングは `{ from: string; to?: string; type?: "string" | "number" | "boolean" | "string[]" }` で、`type` の既定は `"string"` です。マッピング定義が不正ならコンストラクタで throw しますが、*値* が使えない場合は throw しません — `requestContext` は呼び出し側が渡すリクエストデータなので、欠落・空文字・宣言した型に合わない値は単に昇格されません。
57
+ 各マッピングは `{ from: string; to?: string; type?: "string" | "number" | "boolean" | "string[]" }` で、`type` の既定は `"string"` です。`context` 上で完全一致するキーは dot path の走査より優先されます: `"tenant.id"` という文字どおりのフィールドは、`tenant` → `id` より先に読まれます。マッピング定義が不正ならコンストラクタで throw しますが、*値* が使えない場合は throw しません — `requestContext` は呼び出し側が渡すリクエストデータなので、欠落・空文字・宣言した型に合わない値は単に昇格されません。
40
58
 
41
59
  この宣言が信頼境界です — [docs/extending.ja.md](../../docs/extending.ja.md#信頼境界-requestcontext-は呼び出し側のもの) が説明する境界の、既製の守り方がこのコレクターです。`requestContext` は自由形式かつ未検証なので、**宣言していないフィールドは attribute になりません**。dot path は own property のみを辿るため `constructor.name` のような指定は何も読みません。このコレクター自身は語彙を持ち込みません — フィールド名もキー名も運用者が決めるため、[AGENTS.md — Core Vocabulary Scope](../../AGENTS.md#core-vocabulary-scope) の方針を崩さずに実用的なものを提供できます。read-check-write を超える処理(値の導出、外部ストア参照など)が必要な場合は、同節が説明するプロジェクト側の `AttributeCollector` を書いてください。
42
60
 
@@ -75,11 +93,13 @@ new HasPermission(permission: string)
75
93
 
76
94
  - `ruleType`: `"permission"`、`code`: `"no_permission"`
77
95
  - `ATTR_PERMISSIONS`(直接)と `ATTR_ROLES[].permissions`(ロール経由)を確認します。
78
- - ワイルドカードマッチ(大文字・小文字を区別しない):
96
+ - 比較は**大文字・小文字を区別する完全一致**です — `HasScope` が scope に、`DotNotationResourceParser` がリソースに適用しているのと同じ規律で、書かれたものをそのまま比較し、意図を正規化して推測しません。パーサーは大文字・小文字を保持するため、scope ルールにとって `Project:1` と `project:1` が別のリソースであるのとまったく同じく、`Project:1.perm:read` と `project:1.perm:read` は別の permission です。
97
+ - **付与された** permission 内のワイルドカードは尊重されます — 正規化ではなく、書かれたマッチ構造だからです。`*` の前後のリテラル部分は引き続き完全一致で比較されます:
79
98
  - `"*"` はすべての permission に一致。
80
99
  - `"foo*"` はプレフィックスが `foo` の permission に一致。
81
100
  - `"*bar"` はサフィックスが `bar` の permission に一致。
82
101
  - `"foo*bar"` は `foo` で始まり `bar` で終わる permission に一致。
102
+ - 付与された permission に `*` が 2 つ以上含まれる場合は決して一致しません(黙って過剰付与するより拒否する方がよいため)。
83
103
 
84
104
  ### HasScope
85
105
 
@@ -212,9 +232,9 @@ new AttrPairCompare({ a: string, op: "lt" | "le" | "gt" | "ge", b: string, group
212
232
  | `ResourceActionScopeRuleCollector` | `"<action>:<resource.resourceType>"` | `[HasScope(...)]` |
213
233
 
214
234
  `ResourceActionPermissionRuleCollector` にコンストラクタ引数はありません。
215
- `ResourceActionScopeRuleCollector` は `{ scopeless?: "deny" | "skip", allowBareScopeRewrite?: boolean }` を受け取ります。
235
+ `ResourceActionScopeRuleCollector` は `{ scopeless?: "deny" | "skip", allowBareScopeRewrite?: boolean, claim?: string }` を受け取ります — `claim` はトークンが scope を主張していることを示す claim の名前です(既定 `scope`。`PayloadScopeCollector` が読むもの、例えば `scp` に合わせる)。
216
236
 
217
- - `scopeless`(既定 `"deny"`): 既定ではリクエストごとに必ず `HasScope` ルールを生成するため、`scope` claim を持たない
237
+ - `scopeless`(既定 `"deny"`): 既定ではリクエストごとに必ず `HasScope` ルールを生成するため、scope claim(`scope`、または `claim` が指す claim)を持たない
218
238
  トークンはこのルールに落ちます。`"skip"` は scopeless トークンに対してルールを生成しませんが、ルールが 1 つも集まらない
219
239
  リクエストは deny されるため、別のルールグループが認可を担うパイプラインでのみ使ってください。
220
240
  - `allowBareScopeRewrite`(既定 `false`): [`HasScope`](#hasscope) へそのまま渡されます。issuer が `{action}:{resourceType}`
@@ -281,11 +301,12 @@ import { builtinCollectorsModule } from "@o3co/auth.policy-verifier.builtins";
281
301
 
282
302
  | レジストリ | 名前 | ファクトリ |
283
303
  | --- | --- | --- |
284
- | `attributeCollector` | `"PayloadScopeCollector"` | `() => new PayloadScopeCollector()` |
304
+ | `attributeCollector` | `"PayloadScopeCollector"` | `(config) => new PayloadScopeCollector(config)` |
285
305
  | `attributeCollector` | `"PayloadSubjectIdCollector"` | `() => new PayloadSubjectIdCollector()` |
286
306
  | `attributeCollector` | `"StaticPermissionCollector"` | `(config) => new StaticPermissionCollector(config)` |
287
307
  | `attributeCollector` | `"StaticRoleCollector"` | `(config) => new StaticRoleCollector(config)` |
288
308
  | `attributeCollector` | `"RequestContextAttributeCollector"` | `(config) => new RequestContextAttributeCollector(config)` |
309
+ | `attributeCollector` | `"PayloadClaimAttributeCollector"` | `(config) => new PayloadClaimAttributeCollector(config)` |
289
310
  | `ruleCollector` | `"ResourceActionScopeRuleCollector"` | `(config) => new ResourceActionScopeRuleCollector(config)` |
290
311
  | `ruleCollector` | `"ResourceActionPermissionRuleCollector"` | `() => new ResourceActionPermissionRuleCollector()` |
291
312
  | `resourceParser` | `"DotNotationResourceParser"` | `() => new DotNotationResourceParser()` |
package/README.md CHANGED
@@ -232,10 +232,10 @@ All attribute comparison rules follow the same grouping semantics described for
232
232
  | `ResourceActionScopeRuleCollector` | `"<action>:<resource.resourceType>"` | `[HasScope(...)]` |
233
233
 
234
234
  `ResourceActionPermissionRuleCollector` takes no constructor arguments.
235
- `ResourceActionScopeRuleCollector` accepts `{ scopeless?: "deny" | "skip", allowBareScopeRewrite?: boolean }`.
235
+ `ResourceActionScopeRuleCollector` accepts `{ scopeless?: "deny" | "skip", allowBareScopeRewrite?: boolean, claim?: string }` — `claim` names the claim whose presence says the token asserted scopes (default `scope`; set it to what `PayloadScopeCollector` reads, e.g. `scp`).
236
236
 
237
- - `scopeless` (default `"deny"`): it emits the `HasScope` rule for every request, so a token carrying no `scope`
238
- claim fails it. `"skip"` emits no rule for a scopeless token — only use it in a pipeline where another rule
237
+ - `scopeless` (default `"deny"`): it emits the `HasScope` rule for every request, so a token carrying no scope
238
+ claim (`scope`, or the claim `claim` names) fails it. `"skip"` emits no rule for a scopeless token — only use it in a pipeline where another rule
239
239
  group authorizes the request, since a request that collects no rule at all is denied.
240
240
  - `allowBareScopeRewrite` (default `false`): forwarded to [`HasScope`](#hasscope). Set it to `true` only if your
241
241
  issuer emits bare resource names (`project`) rather than `{action}:{resourceType}` scopes (`read:project`).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o3co/auth.policy-verifier.builtins",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "engines": {
@@ -31,12 +31,12 @@
31
31
  }
32
32
  },
33
33
  "dependencies": {
34
- "@o3co/auth.policy-verifier.core": "0.9.0"
34
+ "@o3co/auth.policy-verifier.core": "0.11.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@types/node": "^26.4.0",
38
- "@vitest/coverage-v8": "^4.1.11",
39
- "vitest": "^4.1.11"
37
+ "@types/node": "^26.5.1",
38
+ "@vitest/coverage-v8": "^5.0.0",
39
+ "vitest": "^5.0.0"
40
40
  },
41
41
  "scripts": {
42
42
  "prebuild": "rimraf dist",