Admit list for the informed-decision overview as compact-sitting v3 (FLEX-DEC-2026-017).
list is a separate rule with exact-record scope over the union of the eight
sitting records and the three T03 records, each pinned by approval id, digest and
its own memo version, with a 12-hour MFA window. read and the five acts keep the
v2 rule unchanged — same eight records, same 900-second window — so a list allow
satisfies nothing else.
The operator chose exact-record scope over the consumer's preferred type-wide
scope: the PDP checks no recipient, so type-wide scope with a relaxed window would
have left the consumer's structural match — which the consumer itself says is
not an entitlement — as the only scope. The 12-hour bound replaces the requested
"no bound" so the PDP still states one. The KeyCape stale-timestamp defect is not
worked around; read stays strict.
417 evaluator checks: 168 v2 unchanged, 231 list, 18 proving no act widens to the
T03 records. Also fixes tools/exercise_t03_review_policy.py, which had been
failing since f85479c moved the T03 records to memo version 2 and it still sent
version 1. Test-only.
Not deployed: the pin serving the live review surface changes only with operator
confirmation.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 28468@bnt-lap001
Assistant-Session: c76569b2-6056-4dad-aea4-49cd7a018f5d
This commit is contained in:
parent
1a3f2f7d78
commit
ad7b7f536a
7 changed files with 1928 additions and 8 deletions
20
examples/informed-decision-sitting/list_only_records.json
Normal file
20
examples/informed-decision-sitting/list_only_records.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"memo:SECRETS-WP-0010-T03-apply": {
|
||||
"approval_id": "9935335c-8e9a-566e-a48e-6a5b5f4882eb",
|
||||
"binding_digest": "sha256:03cc5b37437f14e86b686ce4054f976556334eff3fa260b03e8014ed3d9217e5",
|
||||
"memo_version": 2,
|
||||
"label": "t03-apply"
|
||||
},
|
||||
"memo:SECRETS-WP-0010-T03-verify": {
|
||||
"approval_id": "273d6882-6253-5dc9-ac54-544f92ef5e56",
|
||||
"binding_digest": "sha256:72d9267d038c17107c880ffc9009793ce9c70defbddfe2219628854b811a04b2",
|
||||
"memo_version": 2,
|
||||
"label": "t03-verify"
|
||||
},
|
||||
"memo:SECRETS-WP-0010-T03-exec": {
|
||||
"approval_id": "7ba0c13b-68cd-5b3e-9481-42ba9e385e68",
|
||||
"binding_digest": "sha256:f2cf0fb53b740900756ccde5587c3578fcff44beef2f1edab17b9a198a4033d8",
|
||||
"memo_version": 2,
|
||||
"label": "t03-exec"
|
||||
}
|
||||
}
|
||||
|
|
@ -2,11 +2,11 @@
|
|||
id: informed-decision.compact-sitting
|
||||
name: Compact sitting exact-record human review
|
||||
namespace: informed-decision:decision-memo
|
||||
version: v2
|
||||
version: v3
|
||||
status: ready
|
||||
package: flexauth.informed_decision.compact_sitting
|
||||
allow_ttl: 30s
|
||||
actions: [read, acknowledge, accept, return, discuss, decline]
|
||||
actions: [read, acknowledge, accept, return, discuss, decline, list]
|
||||
owner: flex-auth
|
||||
fixtures: [fixtures.json]
|
||||
caring:
|
||||
|
|
@ -26,6 +26,25 @@ and the identity bar are unchanged.
|
|||
It is not an expansion of `examples/informed-decision-t03` / FLEX-WP-0027.
|
||||
`memo:infd-20260914-c01` is omitted (create-client still undecided).
|
||||
|
||||
v3 (2026-09-21, `FLEX-WP-0032`, `FLEX-DEC-2026-017`) adds one action, `list`,
|
||||
for informed-decision's decision overview (`INFD-IN-0008`). It is a separate
|
||||
rule, not an extra member of the act set, and it changes nothing else:
|
||||
|
||||
- `read` and the five acts keep the v2 rule unchanged — the same eight records,
|
||||
the same 900-second MFA window. A `list` allow satisfies no other action.
|
||||
- `list` covers the **union** of this package's eight records and the three
|
||||
`SECRETS-WP-0010-T03-*` records from `informed-decision.t03-review`, each
|
||||
pinned by approval id, binding digest and its own memo version (T03 at 2).
|
||||
Scope stays exact-record: a memo is listable only once someone admitted it.
|
||||
The operator declined resource-type-wide scope because the PDP checks no
|
||||
recipient, and a type-wide rule would have left consumer code — which the
|
||||
consumer itself says is not an entitlement — as the only scope.
|
||||
- `list` carries the same identity bar with a **12-hour** MFA window instead of
|
||||
900 seconds. Looking back at your own outcomes is not binding a new one. The
|
||||
bar is lighter, not absent: the PDP still states a bound.
|
||||
- The KeyCape stale-timestamp defect (`INFD-IN-0005`) is **not** worked around
|
||||
here. `read` stays strict; that defect is key-cape's to fix.
|
||||
|
||||
The identity bar matches T03: authenticated informed-decision caller,
|
||||
`net-kingdom-admins`, fresh KeyCape AAL2 MFA. No permission follows from
|
||||
memo content or presentation state. TokenReview must admit
|
||||
|
|
@ -73,6 +92,31 @@ records := {
|
|||
}
|
||||
}
|
||||
|
||||
list_only_records := {
|
||||
"memo:SECRETS-WP-0010-T03-apply": {
|
||||
"approval_id": "9935335c-8e9a-566e-a48e-6a5b5f4882eb",
|
||||
"binding_digest": "sha256:03cc5b37437f14e86b686ce4054f976556334eff3fa260b03e8014ed3d9217e5",
|
||||
"memo_version": 2
|
||||
},
|
||||
"memo:SECRETS-WP-0010-T03-verify": {
|
||||
"approval_id": "273d6882-6253-5dc9-ac54-544f92ef5e56",
|
||||
"binding_digest": "sha256:72d9267d038c17107c880ffc9009793ce9c70defbddfe2219628854b811a04b2",
|
||||
"memo_version": 2
|
||||
},
|
||||
"memo:SECRETS-WP-0010-T03-exec": {
|
||||
"approval_id": "7ba0c13b-68cd-5b3e-9481-42ba9e385e68",
|
||||
"binding_digest": "sha256:f2cf0fb53b740900756ccde5587c3578fcff44beef2f1edab17b9a198a4033d8",
|
||||
"memo_version": 2
|
||||
}
|
||||
}
|
||||
|
||||
# listable is the union list may range over. Sitting records are memo version 1,
|
||||
# as the act rule pins; T03 records carry their own version (2).
|
||||
listable := object.union(
|
||||
{id: object.union(r, {"memo_version": 1}) | some id, r in records},
|
||||
list_only_records,
|
||||
)
|
||||
|
||||
decision := {"effect": "allow", "reason": "operator_admitted_compact_sitting"} if {
|
||||
input.tenant == "tenant:platform"
|
||||
input.subject.tenant == "tenant:platform"
|
||||
|
|
@ -100,6 +144,33 @@ decision := {"effect": "allow", "reason": "operator_admitted_compact_sitting"} i
|
|||
input.context.approval_id == record.approval_id
|
||||
input.context.approval_binding_digest == record.binding_digest
|
||||
input.action in {"read", "acknowledge", "accept", "return", "discuss", "decline"}
|
||||
} else := {"effect": "allow", "reason": "operator_admitted_overview_list"} if {
|
||||
input.action == "list"
|
||||
input.tenant == "tenant:platform"
|
||||
input.subject.tenant == "tenant:platform"
|
||||
input.subject.type == "human"
|
||||
is_string(input.subject.id)
|
||||
input.subject.id != ""
|
||||
input.subject.attributes.principal_type_source == "authentication-derived"
|
||||
input.subject.attributes.tenant_source in {"registration-supplied", "directory-asserted"}
|
||||
"net-kingdom-admins" in input.subject.attributes.groups
|
||||
assurance := input.subject.attributes.assurance
|
||||
assurance.level == "aal2"
|
||||
assurance.mfa == true
|
||||
assurance.source == "key-cape"
|
||||
assurance.methods == ["pwd", "otp"]
|
||||
is_number(assurance.at)
|
||||
assurance.at > 0
|
||||
age := time.now_ns() / 1000000000 - assurance.at
|
||||
age >= -30
|
||||
age <= 43200
|
||||
input.resource.tenant == "tenant:platform"
|
||||
input.resource.system == "informed-decision"
|
||||
input.resource.type == "decision-memo"
|
||||
record := listable[input.resource.id]
|
||||
input.context.memo_version == record.memo_version
|
||||
input.context.approval_id == record.approval_id
|
||||
input.context.approval_binding_digest == record.binding_digest
|
||||
} else := {"effect": "deny", "reason": "compact_sitting_scope_or_identity_refused"} if {
|
||||
true
|
||||
}
|
||||
|
|
@ -113,4 +184,14 @@ import data.flexauth.informed_decision.compact_sitting
|
|||
test_unknown_request_denied if {
|
||||
compact_sitting.decision.effect == "deny" with input as {}
|
||||
}
|
||||
|
||||
test_listable_is_exact_union if {
|
||||
count(compact_sitting.listable) == count(compact_sitting.records) + count(compact_sitting.list_only_records)
|
||||
}
|
||||
|
||||
test_t03_records_not_in_act_scope if {
|
||||
every id, _ in compact_sitting.list_only_records {
|
||||
not compact_sitting.records[id]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue