ZDDC/zddc/internal/policy/principal_test.go
ZDDC cff840e225 test: lock down elevation gate, .zddc write matrix, audit-log attribution
Four targeted test suites that pin the invariants exercised by the
preceding audit refactor. Closes the coverage gaps identified after the
admin-decider consolidation and the .zddc write-path fix.

internal/policy/principal_test.go (NEW)
  TestAllowActionFromChainP_TruthTable — 11 cases × 5 actions = 55
    assertions covering every (elevated × admin-at-level × action)
    combination. Pins the IsActiveAdmin short-circuit: bypass requires
    BOTH (in admins) AND Elevated; elevation alone confers nothing;
    empty email never matches.
  TestAllowActionFromChainP_AdminScopeDepth — root admin reaches every
    path; subtree admin matches in their own subtree; subtree admin
    does NOT match in a sibling subtree (the chain doesn't carry
    sibling admins lists).
  TestAllowActionFromChainP_BypassWinsOverWorm — elevated admin
    escape hatch in WORM zones, plus the negative control that an
    un-elevated admin does NOT bypass WORM.

internal/handler/auth_invariants_test.go (appended)
  TestInvariant_ZddcPutMatrix — 16 sub-cases across (root / project /
    subtree .zddc) × (root admin / subtree admin / non-admin /
    anonymous) × (elevated / un-elevated). Locks down which principal
    can PUT which .zddc.
  TestInvariant_ZddcDeleteMatrix — 5 DELETE cases.
  TestInvariant_UnelevatedAdminNoSilentBypass — 14 anti-bypass probes:
    every (admin-flavour × probe-path) tuple where an un-elevated
    admin must 403. Single bypass leak → loud test failure.

cmd/zddc-server/main_test.go (appended)
  TestDispatchZddcWriteRouting — full dispatcher path coverage:
    GET/HEAD route to ServeZddcFile (YAML or virtual placeholder);
    PUT/DELETE route through the .zddc-leaf carve-out into
    ServeFileAPI; intermediate .zddc.d/ segments still 404 at the
    guard.

internal/handler/middleware_test.go (appended)
  TestAccessLog_ChainAdminLevelAttribution — 7 cases pinning the
    forensic record: root admin → chain_admin_level=0, subtree admin
    in scope → chain_admin_level=N, subtree admin out of scope → -1,
    un-elevated admin → -1, non-admin → -1, anonymous → -1.
    Cross-checks active_admin == (chain_admin_level >= 0) so a future
    refactor can't desync them.

92 new sub-cases total. Coverage delta on the policy package:
76.1% → 87.2%; AllowActionFromChainP 0% → 100%;
activeAdminForRequest 7% → 68%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 16:29:43 -05:00

280 lines
8.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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`
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 MUST be treated as a normal
// user. They don't carry any baseline ACL grant in this
// fixture, so every action is denied.
{
name: "root admin NOT elevated → no bypass, no ACL grant → all denied",
email: "root@example.com",
elevated: false,
want: want{},
},
{
name: "subtree admin NOT elevated → no bypass, no ACL grant → all denied",
email: "sub@example.com",
elevated: false,
want: want{},
},
// ─── 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.
pUn := zddc.Principal{Email: "root@example.com", Elevated: false}
for _, action := range []string{ActionWrite, ActionDelete, ActionAdmin} {
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)
}
})
}
}