package policy_test import ( "context" "encoding/json" "os" "path/filepath" "testing" "time" "github.com/netkingdom/access-engine/internal/policy" "github.com/netkingdom/access-engine/pkg/api" ) func TestInformedDecisionLifecycle(t *testing.T) { pkg, err := policy.LoadAndValidateFile(context.Background(), filepath.Join("..", "..", "examples", "informed-decision-lifecycle", "policy.md")) if err != nil { t.Fatal(err) } if !pkg.Valid { t.Fatalf("invalid package: %+v", pkg.Validation) } base := map[string]any{ "id": "fixture", "tenant": "tenant:platform", "policy_version": "v1", "subject": map[string]any{"id": "human-one", "type": "human", "tenant": "tenant:platform", "attributes": map[string]any{ "principal_type_source": "authentication-derived", "tenant_source": "registration-supplied", "groups": []any{"net-kingdom-admins"}, "assurance": map[string]any{"level": "aal2", "mfa": true, "source": "key-cape", "methods": []any{"pwd", "otp"}, "at": time.Now().Unix()}, }}, "resource": map[string]any{"id": "memo:new-record", "type": "decision-memo", "system": "informed-decision", "tenant": "tenant:platform"}, "action": "lifecycle.register", "context": map[string]any{"contract": "informed-decision.lifecycle.v1", "memo_version": 1, "command_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "record": map[string]any{"registered": false}}, } human := func(d map[string]any) map[string]any { return d["subject"].(map[string]any) } ctx := func(d map[string]any) map[string]any { return d["context"].(map[string]any) } attrs := func(d map[string]any) map[string]any { return human(d)["attributes"].(map[string]any) } owned := func(d map[string]any) { ctx(d)["record"] = map[string]any{"registered": true, "registered_by": map[string]any{"sub": "human-one", "kind": "person"}, "revision_by": nil, "published_audience": nil, "current_version": 1} d["action"] = "lifecycle.read" } hub := func(d map[string]any) { human(d)["id"] = "system:serviceaccount:state-hub:state-hub" human(d)["type"] = "service" human(d)["attributes"] = map[string]any{"principal_type_source": "authentication-derived", "tenant_source": "registration-supplied", "workload_uid": "verified-uid", "authentication_audience": "informed-decision-lifecycle"} } // FLEX-WP-0035-T05: the readback asks for the application, not a memo. app := func(d map[string]any) { d["action"] = "application.access" d["purpose"] = "readback" d["resource"] = map[string]any{"id": "informed-decision", "type": "application", "system": "informed-decision"} delete(d, "context") delete(attrs(d), "principal_type_source") delete(attrs(d), "tenant_source") } tests := []struct { name string effect string change func(map[string]any) }{ {"readback application access", "allow", app}, {"application access without readback purpose", "deny", func(d map[string]any) { app(d); delete(d, "purpose") }}, {"workload application access", "deny", func(d map[string]any) { hub(d); app(d) }}, {"application access outside group", "deny", func(d map[string]any) { app(d); attrs(d)["groups"] = []any{"other"} }}, {"application access stale MFA", "deny", func(d map[string]any) { app(d) attrs(d)["assurance"].(map[string]any)["at"] = time.Now().Unix() - 43201 }}, {"application access without otp", "deny", func(d map[string]any) { app(d); attrs(d)["assurance"].(map[string]any)["methods"] = []any{"pwd"} }}, {"application access other application", "deny", func(d map[string]any) { app(d) d["resource"].(map[string]any)["id"] = "markitect" }}, {"application access foreign tenant", "deny", func(d map[string]any) { app(d); d["tenant"] = "tenant:foreign" }}, {"human registration", "allow", func(d map[string]any) {}}, {"workload registration", "allow", hub}, {"foreign workload", "deny", func(d map[string]any) { hub(d); human(d)["id"] = "system:serviceaccount:foreign:foreign" }}, {"wrong workload audience", "deny", func(d map[string]any) { hub(d); attrs(d)["authentication_audience"] = "informed-decision-intake" }}, {"missing workload UID", "deny", func(d map[string]any) { hub(d); delete(attrs(d), "workload_uid") }}, {"wrong tenant", "deny", func(d map[string]any) { d["tenant"] = "tenant:foreign" }}, {"outside human group", "deny", func(d map[string]any) { attrs(d)["groups"] = []any{"other"} }}, {"stale author MFA", "deny", func(d map[string]any) { attrs(d)["assurance"].(map[string]any)["at"] = time.Now().Unix() - 43201 }}, {"future author MFA", "deny", func(d map[string]any) { attrs(d)["assurance"].(map[string]any)["at"] = time.Now().Unix() + 301 }}, {"asserted identity", "deny", func(d map[string]any) { attrs(d)["principal_type_source"] = "asserted" }}, {"owner read", "allow", owned}, {"foreign unpublished read", "deny", func(d map[string]any) { owned(d); human(d)["id"] = "another-admin" }}, {"published collaborator", "allow", func(d map[string]any) { owned(d) human(d)["id"] = "another-admin" ctx(d)["record"].(map[string]any)["published_audience"] = map[string]any{"subjects": []any{}, "groups": []any{"net-kingdom-admins"}} }}, {"new revision editor", "allow", func(d map[string]any) { owned(d) human(d)["id"] = "editor" ctx(d)["record"].(map[string]any)["revision_by"] = map[string]any{"sub": "editor", "kind": "person"} }}, {"unknown action", "deny", func(d map[string]any) { owned(d); d["action"] = "lifecycle.delete" }}, {"agent acceptance", "deny", func(d map[string]any) { hub(d); d["action"] = "accept" }}, {"human acceptance excluded", "deny", func(d map[string]any) { owned(d); d["action"] = "lifecycle.accept" }}, {"publish initial group", "allow", func(d map[string]any) { owned(d) d["action"] = "lifecycle.publish" ctx(d)["publication_audience"] = map[string]any{"subjects": []any{}, "groups": []any{"net-kingdom-admins"}} }}, {"publish other group", "deny", func(d map[string]any) { owned(d) d["action"] = "lifecycle.publish" ctx(d)["publication_audience"] = map[string]any{"subjects": []any{}, "groups": []any{"other"}} }}, {"review routing", "allow", func(d map[string]any) { owned(d) d["action"] = "lifecycle.request_review" ctx(d)["review_routing"] = map[string]any{"submitter": "state-hub", "memo_class": "decision-record", "review_group": "net-kingdom-admins"} }}, {"wrong review routing", "deny", func(d map[string]any) { owned(d) d["action"] = "lifecycle.request_review" ctx(d)["review_routing"] = map[string]any{"submitter": "unknown", "memo_class": "decision-record", "review_group": "net-kingdom-admins"} }}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { encoded, _ := json.Marshal(base) var d map[string]any json.Unmarshal(encoded, &d) tc.change(d) encoded, _ = json.Marshal(d) var request api.CheckRequest if err := json.Unmarshal(encoded, &request); err != nil { t.Fatal(err) } got, err := pkg.Evaluate(context.Background(), request) if err != nil { t.Fatal(err) } if string(got.Effect) != tc.effect { t.Fatalf("got %+v, want %s", got, tc.effect) } }) } } func TestInformedDecisionLifecycleReleasePins(t *testing.T) { for _, name := range []string{"informed-decision-lifecycle", "informed-decision-lifecycle-review"} { dir := filepath.Join(repoRoot(t), "examples", name) pkg, err := policy.LoadAndValidateFile(context.Background(), filepath.Join(dir, "policy.md")) if err != nil || !pkg.Valid { t.Fatalf("%s invalid: %v %+v", name, err, pkg) } raw, err := os.ReadFile(filepath.Join(dir, "pin.json")) if err != nil { t.Fatal(err) } var pin map[string]string if err := json.Unmarshal(raw, &pin); err != nil { t.Fatal(err) } if pin["package_digest"] != pkg.Digest() || pin["version"] != pkg.Metadata.Version || pin["package"] != pkg.Metadata.ID { t.Fatalf("%s stale pin: %+v", name, pin) } } }