FLEX-WP-0035-T05: informed-decision lifecycle v2 declares application.access
- application.access allows only with purpose=readback and the authoring human bar; no workload path, no audience or client-scoped claims - v1 operations unchanged; pin.json moves to v2; live pin stays v1 until T04 - examples/application-readback: readback catalogue with informed-decision's key-cape client binding; integration test over the shipped files Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 609675@bnt-lap001 Assistant-Session: 4987a36e-eb38-4b10-803c-15a29cd78212
This commit is contained in:
parent
a4103272e1
commit
b5da745063
10 changed files with 207 additions and 12 deletions
17
examples/application-readback/README.md
Normal file
17
examples/application-readback/README.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Application access readback catalogue
|
||||
|
||||
The readback pin's own registry (`FLEX-WP-0035`, `FLEX-DEC-2026-019`). It holds
|
||||
one entry per application that user-engine may list: id, display name, and the
|
||||
key-cape client its users sign in through. The entries are descriptive only and
|
||||
never decide an allow. Allow comes from each application's own package, which
|
||||
must declare `application.access`.
|
||||
|
||||
Participants' registries are loaded alongside this one, and their packages are
|
||||
loaded at the same files their own pins run:
|
||||
|
||||
| System | Package | Registry |
|
||||
| --- | --- | --- |
|
||||
| `informed-decision` | `examples/informed-decision-lifecycle/policy.md` (v2) | `examples/informed-decision-lifecycle/registry.json` |
|
||||
|
||||
`client_binding` sources: `informed-decision/docs/keycape-client-registration.md`
|
||||
(`client_id`) and `informed-decision/informed_decision/oidc.py` (issuer).
|
||||
13
examples/application-readback/registry_snapshot.json
Normal file
13
examples/application-readback/registry_snapshot.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"systems": [
|
||||
{
|
||||
"id": "informed-decision",
|
||||
"name": "Informed Decision",
|
||||
"description": "Decision memos: authoring, collaboration and human review.",
|
||||
"client_binding": {
|
||||
"issuer": "https://kc.coulomb.social",
|
||||
"client_id": "informed-decision-approver"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -17,5 +17,42 @@
|
|||
"expect": {
|
||||
"effect": "deny"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "application-access-without-readback-purpose-denied",
|
||||
"request": {
|
||||
"id": "application-access-enforcement",
|
||||
"tenant": "tenant:platform",
|
||||
"subject": {
|
||||
"id": "human-one",
|
||||
"type": "human",
|
||||
"tenant": "tenant:platform",
|
||||
"attributes": {
|
||||
"groups": [
|
||||
"net-kingdom-admins"
|
||||
],
|
||||
"assurance": {
|
||||
"level": "aal2",
|
||||
"mfa": true,
|
||||
"source": "key-cape",
|
||||
"methods": [
|
||||
"pwd",
|
||||
"otp"
|
||||
],
|
||||
"at": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"action": "application.access",
|
||||
"resource": {
|
||||
"id": "informed-decision",
|
||||
"type": "application",
|
||||
"system": "informed-decision"
|
||||
}
|
||||
},
|
||||
"expect": {
|
||||
"effect": "deny",
|
||||
"reason": "lifecycle_scope_or_identity_refused"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"package": "informed-decision.lifecycle",
|
||||
"version": "v1",
|
||||
"package_digest": "sha256:cee6a79f74e81d0d389a2c2e2451a92be3434ea9dfcee4986e8c7e6cfdbb1827"
|
||||
"version": "v2",
|
||||
"package_digest": "sha256:da01e045fc108adec6b489e5d51f7e57366793016c5a64776963780a6aae457c"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
id: informed-decision.lifecycle
|
||||
name: Decision lifecycle authoring and collaboration
|
||||
namespace: informed-decision:decision-memo
|
||||
version: v1
|
||||
version: v2
|
||||
status: ready
|
||||
package: flexauth.informed_decision.lifecycle
|
||||
allow_ttl: 30s
|
||||
actions: [lifecycle.register, lifecycle.read, lifecycle.search, lifecycle.revise, lifecycle.source, lifecycle.evaluation, lifecycle.comment, lifecycle.events, lifecycle.packet, lifecycle.publish, lifecycle.request_review, lifecycle.outcomes]
|
||||
actions: [lifecycle.register, lifecycle.read, lifecycle.search, lifecycle.revise, lifecycle.source, lifecycle.evaluation, lifecycle.comment, lifecycle.events, lifecycle.packet, lifecycle.publish, lifecycle.request_review, lifecycle.outcomes, application.access]
|
||||
owner: flex-auth
|
||||
fixtures: [fixtures.json]
|
||||
caring:
|
||||
|
|
@ -43,6 +43,23 @@ identity. Authentication-derived values still come from the enforced consumer
|
|||
boundary (KeyCape sessions or CA-verified TokenReview). This remains a consumer
|
||||
claim trust boundary; a registry declaration is not independent verification.
|
||||
|
||||
## Application access readback (v2)
|
||||
|
||||
v2 adds one action, `application.access`, for the advisory readback that tells
|
||||
user-engine whether to list informed-decision for a signed-in person
|
||||
(`FLEX-WP-0035-T05`, `FLEX-DEC-2026-019`). v1's twelve operations are
|
||||
unchanged.
|
||||
|
||||
The rule is the same `human` bar authoring uses: net-kingdom-admins, KeyCape
|
||||
AAL2 `pwd`+`otp` within 12 hours, tenant platform. It reads only realm-level
|
||||
IAM Profile v0.3 claims (groups and normalized `assurance`). It reads no
|
||||
audience, authorized party or client-scoped role, because the forwarded
|
||||
claims were issued for user-engine's client. The workload path is excluded:
|
||||
it turns on `authentication_audience`, and the readback lists applications for
|
||||
people. It also requires `purpose == "readback"`, so it can never allow on an
|
||||
enforcement pin, which refuses a purpose-bearing request. The allow grants
|
||||
nothing inside informed-decision; every operation is still checked above.
|
||||
|
||||
```rego
|
||||
import rego.v1
|
||||
|
||||
|
|
@ -168,7 +185,20 @@ operation if {
|
|||
routing.review_group == "net-kingdom-admins"
|
||||
}
|
||||
|
||||
decision := {"effect":"allow", "reason":"lifecycle_collaboration"} if {
|
||||
application_access if {
|
||||
input.action == "application.access"
|
||||
input.purpose == "readback"
|
||||
input.tenant == "tenant:platform"
|
||||
input.subject.tenant == input.tenant
|
||||
input.resource.system == "informed-decision"
|
||||
input.resource.type == "application"
|
||||
input.resource.id == "informed-decision"
|
||||
human
|
||||
}
|
||||
|
||||
decision := {"effect":"allow", "reason":"application_access_readback"} if {
|
||||
application_access
|
||||
} else := {"effect":"allow", "reason":"lifecycle_collaboration"} if {
|
||||
common
|
||||
identity
|
||||
operation
|
||||
|
|
@ -183,4 +213,12 @@ import data.flexauth.informed_decision.lifecycle
|
|||
test_empty_denied if {
|
||||
lifecycle.decision.effect == "deny" with input as {}
|
||||
}
|
||||
|
||||
test_application_access_needs_readback_purpose if {
|
||||
lifecycle.decision.effect == "deny" with input as {
|
||||
"action": "application.access", "tenant": "tenant:platform",
|
||||
"subject": {"type": "human", "tenant": "tenant:platform"},
|
||||
"resource": {"id": "informed-decision", "type": "application", "system": "informed-decision"}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue