package policy_test import ( "bytes" "context" "encoding/json" "os" "os/exec" "path/filepath" "strings" "testing" "time" "github.com/netkingdom/flex-auth/internal/policy" "github.com/netkingdom/flex-auth/pkg/api" ) func TestInformedDecisionAttributeAdmission(t *testing.T) { pkg, err := policy.LoadAndValidateFile(context.Background(), sittingPolicyPath(t)) if err != nil { t.Fatalf("LoadAndValidateFile: %v", err) } if !pkg.Valid { t.Fatalf("package invalid: %+v", pkg.Validation) } if pkg.Metadata.Version != "v11" { t.Fatalf("version = %s; want v11", pkg.Metadata.Version) } if strings.Contains(pkg.RegoModule, "view_hash") { t.Fatal("rule module reads view_hash") } if !strings.Contains(pkg.RegoModule, "admitted_submitters := set()") || !strings.Contains(pkg.RegoModule, "admitted_classes := set()") { t.Fatal("shipped admitted sets are not empty") } raw, err := os.ReadFile(filepath.Join(sittingDir(t), "attribute_admission_v8.json")) if err != nil { t.Fatal(err) } var doc struct { PolicyVersion string `json:"policy_version"` Cases []struct { ID string `json:"id"` Kind string `json:"kind"` Assurance string `json:"assurance"` Request api.CheckRequest `json:"request"` Expect api.DecisionExpectation `json:"expect"` } `json:"cases"` } if err := json.Unmarshal(raw, &doc); err != nil { t.Fatal(err) } if doc.PolicyVersion != "v11" { t.Fatalf("fixture policy_version = %s", doc.PolicyVersion) } exactKeys := map[string]bool{"memo_version": true, "approval_id": true, "approval_binding_digest": true} attributeKeys := map[string]bool{ "review_group": true, "memo_class": true, "intake_submitter": true, "approval_requester": true, "approval_human_control": true, "memo_version": true, "approval_id": true, "approval_binding_digest": true, } for _, fixture := range doc.Cases { request := fixture.Request stampAssurance(t, &request, fixture.Assurance) if _, ok := request.Context["view_hash"]; ok { t.Fatalf("%s carries view_hash", fixture.ID) } switch fixture.Kind { case "exact": assertKeys(t, fixture.ID, request.Context, exactKeys) case "attribute": assertKeys(t, fixture.ID, request.Context, attributeKeys) default: t.Fatalf("%s kind %q", fixture.ID, fixture.Kind) } got, err := pkg.Evaluate(context.Background(), request) if err != nil { t.Fatalf("%s: %v", fixture.ID, err) } if got.Effect != fixture.Expect.Effect || got.Reason != fixture.Expect.Reason { t.Fatalf("%s: got %s %s; want %s %s", fixture.ID, got.Effect, got.Reason, fixture.Expect.Effect, fixture.Expect.Reason) } if fixture.ID == "exact-record-allow" { withHash := request withHash.Context = copyContext(request.Context) withHash.Context["view_hash"] = "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" hashed, err := pkg.Evaluate(context.Background(), withHash) if err != nil { t.Fatal(err) } if hashed.Effect != got.Effect || hashed.Reason != got.Reason { t.Fatalf("view_hash changed exact decision to %s %s", hashed.Effect, hashed.Reason) } } } } func TestSittingImagePinMatchesRecordedBuild(t *testing.T) { raw, err := os.ReadFile(filepath.Join(repoRoot(t), "values", "informed-decision-sitting.yaml")) if err != nil { t.Fatal(err) } text := string(raw) // values/informed-decision-sitting.yaml records the v11 image of 1aae59f. // The earlier v10 pin (58026ea) is no longer the baseline. if !strings.Contains(text, "sha256:feae06d5cd6c68296437d9df344ab76e44c178b8671ac76bf2d6e83e8ac1f645") { t.Fatal("sitting image digest differs from the recorded v11 build") } if !strings.Contains(text, "/opt/flex-auth/examples/informed-decision-sitting/policy.md") { t.Fatal("sitting policy path changed") } } func TestAdmissionSetGenerator(t *testing.T) { root := repoRoot(t) check := exec.Command("python3", "tools/gen_informed_decision_admission.py", "--check") check.Dir = root if out, err := check.CombinedOutput(); err != nil { t.Fatalf("drift check: %v\n%s", err, out) } dir := t.TempDir() policyPath := filepath.Join(dir, "policy.md") shipped := sittingPolicyPath(t) source, err := os.ReadFile(shipped) if err != nil { t.Fatal(err) } if err := os.WriteFile(policyPath, source, 0o644); err != nil { t.Fatal(err) } registry := filepath.Join(dir, "submitters.json") body := `{ "system:serviceaccount:state-hub:state-hub": { "submitter_id": "state-hub", "memo_classes": ["credential-lane", "adr-accept"], "review_groups": ["net-kingdom-admins"], "daily_limit": 1 } }` if err := os.WriteFile(registry, []byte(body), 0o644); err != nil { t.Fatal(err) } drift := exec.Command("python3", filepath.Join(root, "tools/gen_informed_decision_admission.py"), "--source", registry, "--policy", policyPath, "--check") if err := drift.Run(); err == nil { t.Fatal("non-empty registry matched the empty shipped sets") } after, err := os.ReadFile(policyPath) if err != nil { t.Fatal(err) } if !bytes.Equal(after, source) { t.Fatal("--check wrote the policy") } write := exec.Command("python3", filepath.Join(root, "tools/gen_informed_decision_admission.py"), "--source", registry, "--policy", policyPath) if out, err := write.CombinedOutput(); err != nil { t.Fatalf("generate: %v\n%s", err, out) } generated, err := os.ReadFile(policyPath) if err != nil { t.Fatal(err) } text := string(generated) if !strings.Contains(text, `"adr-accept",`) || !strings.Contains(text, `"credential-lane",`) || !strings.Contains(text, `"state-hub",`) { t.Fatalf("generated sets missing:\n%s", text) } if !bytes.Equal(mustRead(t, shipped), source) { t.Fatal("generator wrote the shipped policy") } again := exec.Command("python3", filepath.Join(root, "tools/gen_informed_decision_admission.py"), "--source", registry, "--policy", policyPath, "--check") if out, err := again.CombinedOutput(); err != nil { t.Fatalf("regenerated policy drifted: %v\n%s", err, out) } bad := filepath.Join(dir, "bad.json") if err := os.WriteFile(bad, []byte("[]"), 0o644); err != nil { t.Fatal(err) } refused := exec.Command("python3", filepath.Join(root, "tools/gen_informed_decision_admission.py"), "--source", bad, "--policy", policyPath) if err := refused.Run(); err == nil { t.Fatal("malformed registry was accepted") } kept, err := os.ReadFile(policyPath) if err != nil { t.Fatal(err) } if !bytes.Equal(kept, generated) { t.Fatal("refused generate changed the policy") } } func stampAssurance(t *testing.T, request *api.CheckRequest, mode string) { t.Helper() assurance, _ := request.Subject.Attributes["assurance"].(map[string]any) if assurance == nil { t.Fatal("assurance missing") } switch mode { case "now": assurance["at"] = time.Now().Unix() case "stale": assurance["at"] = int64(1) default: t.Fatalf("assurance mode %q", mode) } } func assertKeys(t *testing.T, id string, ctx map[string]any, want map[string]bool) { t.Helper() if len(ctx) != len(want) { t.Fatalf("%s context keys = %v", id, ctx) } for key := range want { if _, ok := ctx[key]; !ok { t.Fatalf("%s missing %s", id, key) } } } func copyContext(in map[string]any) map[string]any { out := make(map[string]any, len(in)+1) for key, value := range in { out[key] = value } return out } func sittingDir(t *testing.T) string { t.Helper() return filepath.Join(repoRoot(t), "examples", "informed-decision-sitting") } func sittingPolicyPath(t *testing.T) string { t.Helper() return filepath.Join(sittingDir(t), "policy.md") } func repoRoot(t *testing.T) string { t.Helper() return filepath.Join("..", "..") } func mustRead(t *testing.T, path string) []byte { t.Helper() data, err := os.ReadFile(path) if err != nil { t.Fatal(err) } return data }