package policy import ( "context" "testing" "codeberg.org/VARASYS/ZDDC/zddc/internal/zddc" ) // TestAllowActionFromChainP_TruthTable pins the principal-aware decider // across the full {elevated × admin-at-level-N × action} cross-product. // This is the single bypass site that consolidates every former // scattered IsAdmin/IsSubtreeAdmin/CanEditZddc check in handler code, // so its semantics must be locked in by an exhaustive table. // // Invariants pinned: // // 1. Admin bypass requires BOTH (Email in admins:) AND Elevated. // - In admins + elevated → bypass (any action returns true) // - In admins + un-elevated → no bypass (falls through to ACL) // - Not in admins + elevated → no bypass // - Empty email + elevated → no bypass (gate() rejects empty) // // 2. Bypass is action-agnostic: ActionRead, ActionWrite, ActionCreate, // ActionDelete, ActionAdmin all behave the same way under bypass. // // 3. Admin authority at ANY level on the chain confers bypass // (root admin gets bypass even on deep paths; subtree admin // declared at level N gets bypass for level ≥ N). // // 4. With no bypass, the cascade ACL governs: // - rwcd grant → ActionRead/Write/Create/Delete succeed, ActionAdmin denied // - no grant + has_any_file → all actions denied // - empty chain → all actions allowed (public default) func TestAllowActionFromChainP_TruthTable(t *testing.T) { // Chain shape used throughout: root admins:[root@example.com] + // level 1 admins:[sub@example.com] + level 1 ACL allowing // staff@example.com rwcd. chain := zddc.PolicyChain{ HasAnyFile: true, Levels: []zddc.ZddcFile{ {Admins: []string{"root@example.com"}}, { Admins: []string{"sub@example.com"}, ACL: zddc.ACLRules{Permissions: map[string]string{ "staff@example.com": "rwcd", }}, }, }, } type want struct { read, write, create, deleteV, adminV bool } allActions := want{true, true, true, true, true} noAdmin := want{true, true, true, true, false} // staff has rwcd but no `a` configOnly := want{adminV: true} // standing config-edit, nothing else cases := []struct { name string email string elevated bool want want }{ // ─── BYPASS PATH ──────────────────────────────────────────── { name: "root admin elevated → bypass on every action", email: "root@example.com", elevated: true, want: allActions, }, { name: "subtree admin elevated → bypass on every action", email: "sub@example.com", elevated: true, want: allActions, }, // ─── ELEVATION GATE ───────────────────────────────────────── // An admin who hasn't elevated gets the WORM/destructive bypass // on NOTHING — but config-edit (the `a` verb) is a STANDING // permission, so ActionAdmin is allowed while r/w/c/d (no ACL // grant in this fixture) stay denied. Elevation is additive. { name: "root admin NOT elevated → standing config-edit only", email: "root@example.com", elevated: false, want: configOnly, }, { name: "subtree admin NOT elevated → standing config-edit only", email: "sub@example.com", elevated: false, want: configOnly, }, // ─── NON-ADMIN PATHS ──────────────────────────────────────── { name: "non-admin with rwcd grant → ACL governs, admin denied", email: "staff@example.com", elevated: false, want: noAdmin, }, { name: "non-admin elevated → elevation alone confers nothing", email: "staff@example.com", elevated: true, want: noAdmin, }, { name: "stranger denied across the board", email: "rando@example.com", elevated: false, want: want{}, }, { name: "stranger elevated still denied", email: "rando@example.com", elevated: true, want: want{}, }, // ─── ANONYMOUS / DEGENERATE ───────────────────────────────── { name: "empty email + elevated → gate rejects, no bypass", email: "", elevated: true, want: want{}, }, { name: "empty email + not elevated → denied", email: "", elevated: false, want: want{}, }, } d := &InternalDecider{} ctx := context.Background() for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { p := zddc.Principal{Email: tc.email, Elevated: tc.elevated} check := func(action string, want bool) { t.Helper() got, err := AllowActionFromChainP(ctx, d, chain, p, "/sub/file", action) if err != nil { t.Fatalf("%s: unexpected error: %v", action, err) } if got != want { t.Errorf("%s: got %v, want %v", action, got, want) } } check(ActionRead, tc.want.read) check(ActionWrite, tc.want.write) check(ActionCreate, tc.want.create) check(ActionDelete, tc.want.deleteV) check(ActionAdmin, tc.want.adminV) }) } } // TestAllowActionFromChainP_AdminScopeDepth: admin authority at the // root level cascades to every depth; subtree admin authority declared // at level N applies only when level N is on the queried chain. The // decider doesn't synthesise admin authority — it derives it from // IsAdminForChain, which walks the chain it was given. func TestAllowActionFromChainP_AdminScopeDepth(t *testing.T) { rootOnly := zddc.PolicyChain{ HasAnyFile: true, Levels: []zddc.ZddcFile{ {Admins: []string{"root@example.com"}}, }, } rootPlusProject := zddc.PolicyChain{ HasAnyFile: true, Levels: []zddc.ZddcFile{ {Admins: []string{"root@example.com"}}, {Admins: []string{"alice@example.com"}}, }, } siblingChain := zddc.PolicyChain{ HasAnyFile: true, Levels: []zddc.ZddcFile{ {Admins: []string{"root@example.com"}}, // Sibling project — alice is NOT in this chain's admins. {Admins: []string{"bob@example.com"}}, }, } d := &InternalDecider{} ctx := context.Background() cases := []struct { name string chain zddc.PolicyChain email string path string wantPut bool }{ { name: "root admin reaches a root-only path", chain: rootOnly, email: "root@example.com", path: "/file", wantPut: true, }, { name: "root admin reaches a deep path", chain: rootPlusProject, email: "root@example.com", path: "/Project-A/file", wantPut: true, }, { name: "subtree admin reaches their own subtree", chain: rootPlusProject, email: "alice@example.com", path: "/Project-A/file", wantPut: true, }, { name: "subtree admin does NOT reach a sibling subtree", chain: siblingChain, email: "alice@example.com", path: "/Project-B/file", wantPut: false, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { p := zddc.Principal{Email: tc.email, Elevated: true} got, _ := AllowActionFromChainP(ctx, d, tc.chain, p, tc.path, ActionWrite) if got != tc.wantPut { t.Errorf("AllowActionFromChainP write: got %v, want %v", got, tc.wantPut) } }) } } // TestAllowActionFromChainP_BypassWinsOverWorm: an elevated admin's // bypass fires before WORM evaluation, so a mis-filed document under // received/ or issued/ can still be corrected. This is the explicit // human escape hatch documented in the policy package comment. func TestAllowActionFromChainP_BypassWinsOverWorm(t *testing.T) { trueP := true chain := zddc.PolicyChain{ HasAnyFile: true, Levels: []zddc.ZddcFile{ {Admins: []string{"root@example.com"}}, { // WORM zone (received/issued style). Without admin bypass, // every write would be stripped. Worm: []string{"_doc_controller"}, ACL: zddc.ACLRules{Inherit: &trueP}, }, }, } d := &InternalDecider{} ctx := context.Background() p := zddc.Principal{Email: "root@example.com", Elevated: true} for _, action := range []string{ActionRead, ActionWrite, ActionCreate, ActionDelete, ActionAdmin} { t.Run("elevated admin in WORM zone — "+action, func(t *testing.T) { got, _ := AllowActionFromChainP(ctx, d, chain, p, "/received/x", action) if !got { t.Errorf("elevated admin %s denied inside WORM zone", action) } }) } // Negative control: same principal un-elevated must NOT bypass WORM for // DATA ops. Write/Delete (and Create) of records stay clamped — those // are the destructive overrides elevation exists for. pUn := zddc.Principal{Email: "root@example.com", Elevated: false} for _, action := range []string{ActionWrite, ActionDelete} { t.Run("un-elevated admin in WORM zone — "+action, func(t *testing.T) { got, _ := AllowActionFromChainP(ctx, d, chain, pUn, "/received/x", action) if got { t.Errorf("un-elevated admin %s allowed inside WORM zone (bypass leaked)", action) } }) } // EXCEPTION: ActionAdmin (config-edit) is a STANDING permission and // transcends the WORM clamp — a subtree admin may fix the .zddc that // governs a WORM zone without elevating. This grants only VerbA, never // write/delete of the WORM records themselves (asserted just above). if got, _ := AllowActionFromChainP(ctx, d, chain, pUn, "/received/.zddc", ActionAdmin); !got { t.Errorf("un-elevated admin ActionAdmin denied in WORM zone; config-edit should be standing") } }