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
|
|
@ -12,6 +12,7 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/netkingdom/flex-auth/internal/audit"
|
||||
"github.com/netkingdom/flex-auth/internal/callerauth"
|
||||
|
|
@ -298,3 +299,47 @@ func TestReadbackShapesMatchSchemas(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FLEX-WP-0035-T05: the shipped catalogue and informed-decision lifecycle v2
|
||||
// answer the readback the way user-engine will ask it.
|
||||
func TestReadbackInformedDecisionCatalogue(t *testing.T) {
|
||||
examples := filepath.Join("..", "..", "examples")
|
||||
service, err := buildReadback(context.Background(),
|
||||
[]string{
|
||||
filepath.Join(examples, "application-readback", "registry_snapshot.json"),
|
||||
filepath.Join(examples, "informed-decision-lifecycle", "registry.json"),
|
||||
},
|
||||
map[string]string{"informed-decision": filepath.Join(examples, "informed-decision-lifecycle", "policy.md")})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
subject := func(groups ...any) readback.Request {
|
||||
return readback.Request{
|
||||
Tenant: "tenant:platform",
|
||||
Subject: api.SubjectRef{ID: "human-one", Type: "human", Tenant: "tenant:platform", Attributes: map[string]any{
|
||||
"issuer": "https://kc.coulomb.social", "roles": []any{}, "tenant_roles": []any{}, "scopes": []any{}, "groups": groups,
|
||||
"assurance": map[string]any{"level": "aal2", "mfa": true, "source": "key-cape", "methods": []any{"pwd", "otp"}, "at": time.Now().Unix()},
|
||||
}},
|
||||
Systems: []string{"informed-decision"},
|
||||
}
|
||||
}
|
||||
for name, tc := range map[string]struct {
|
||||
request readback.Request
|
||||
effect api.DecisionEffect
|
||||
}{
|
||||
"admin": {subject("net-kingdom-admins"), api.DecisionEffectAllow},
|
||||
"non-admin": {subject("staff"), api.DecisionEffectDeny},
|
||||
} {
|
||||
response, err := service.Evaluate(context.Background(), tc.request)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := response.Results[0]; got.Effect != tc.effect || got.PolicyPackage != "informed-decision.lifecycle" {
|
||||
t.Fatalf("%s: %+v", name, got)
|
||||
}
|
||||
}
|
||||
systems := service.Systems().Systems
|
||||
if len(systems) != 1 || !systems[0].Readback || systems[0].ClientBinding == nil || systems[0].ClientBinding.ClientID != "informed-decision-approver" {
|
||||
t.Fatalf("catalogue = %+v", systems)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue