feat(policy): IsActiveAdmin field + AllowActionFromChainP entry point

Lays the rails for the consolidation refactor — the decider gains a
single admin-bypass branch at the top of InternalDecider.Allow, and a
new principal-aware entry point computes IsActiveAdmin from chain +
Principal.Elevated. No caller uses the new path yet, so behavior is
unchanged; lock-in tests stay green.

  AllowInput.User.IsActiveAdmin bool   // caller-computed bypass flag
  AllowActionFromChainP(ctx, d, chain, p, path, action) (bool, error)

The decider's branch:

  if input.User.IsActiveAdmin { return true, nil }

is the ONLY admin escape hatch in the package. Strict-ancestor rule
for .zddc edits is preserved inside AllowActionFromChainP via
IsAdminForChain(chain, email, excludeLeaf=true) when action==ActionAdmin.

Email-only entry points (AllowFromChain, AllowActionFromChain) leave
IsActiveAdmin=false implicitly — they're for read-path callers that
don't need admin bypass (directory listing, archive index, profile
read endpoints).

Next commits: migrate authorizeAction and plan-review's pre-flight
to AllowActionFromChainP, then delete the scattered IsAdmin/
IsSubtreeAdmin/CanEditZddc early-outs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZDDC 2026-05-18 09:17:44 -05:00
parent 1c0777a847
commit 465d2f605c

View file

@ -73,6 +73,20 @@ import (
type AllowInput struct { type AllowInput struct {
User struct { User struct {
Email string `json:"email"` Email string `json:"email"`
// IsActiveAdmin is true when the caller has admin authority on
// THIS chain AND has opted into admin powers for this request.
// The CALLER is responsible for computing it (it depends on the
// chain and on Principal.Elevated, both known at the call site);
// the decider consults it as a short-circuit at the top of
// Allow. This is the single bypass point — every write that
// should ignore WORM/ACL for an elevated admin flows through it,
// every read that should ditto.
//
// Callers using the email-only entry points (AllowFromChain,
// AllowActionFromChain) get IsActiveAdmin=false implicitly —
// they're saying "ignore admin bypass for this lookup." Callers
// that want the bypass use AllowActionFromChainP.
IsActiveAdmin bool `json:"is_active_admin,omitempty"`
} `json:"user"` } `json:"user"`
Path string `json:"path"` Path string `json:"path"`
Action string `json:"action,omitempty"` Action string `json:"action,omitempty"`
@ -219,6 +233,17 @@ func (d *InternalDecider) Allow(_ context.Context, input AllowInput) (bool, erro
verb := actionVerb(input.Action) verb := actionVerb(input.Action)
email := input.User.Email email := input.User.Email
// Single admin-bypass site. The caller has already verified that
// the principal (a) holds an admins: grant somewhere in this chain
// and (b) has opted into admin powers (Elevated). When both are
// true, any action is permitted — WORM zones included — preserving
// the human escape hatch for mis-filed documents. No other site in
// the codebase grants admin authority; every write that should
// bypass ACL/WORM flows through this one branch.
if input.User.IsActiveAdmin {
return true, nil
}
// WORM zone: a directory whose cascade declares `worm:` (see // WORM zone: a directory whose cascade declares `worm:` (see
// defaults.zddc.yaml — archive/<party>/received and issued carry // defaults.zddc.yaml — archive/<party>/received and issued carry
// `worm: {}`) is write-locked. Inside it, the effective verbs // `worm: {}`) is write-locked. Inside it, the effective verbs
@ -354,6 +379,31 @@ func AllowActionFromChain(ctx context.Context, d Decider, chain zddc.PolicyChain
return d.Allow(ctx, in) return d.Allow(ctx, in)
} }
// AllowActionFromChainP is the principal-aware entry point. Computes
// IsActiveAdmin from the chain + Principal.Elevated and threads it
// into AllowInput, so the decider's single admin-bypass branch fires
// when (and only when) the caller actually holds elevated admin
// authority on this chain.
//
// Strict-ancestor rule for .zddc edits: action == ActionAdmin signals
// a .zddc write, and IsAdminForChain is called with excludeLeaf=true
// so the leaf .zddc's own admins entry cannot authorize editing the
// file that grants it. Other actions use the full chain walk.
//
// Use this entry point in write-path handlers (file API, plan-review,
// accept-transmittal). Read-path callers that don't need admin
// bypass can stay on AllowActionFromChain / AllowFromChain — they
// implicitly leave IsActiveAdmin=false.
func AllowActionFromChainP(ctx context.Context, d Decider, chain zddc.PolicyChain, p zddc.Principal, path, action string) (bool, error) {
excludeLeaf := action == ActionAdmin
isAdmin := p.Elevated && p.Email != "" &&
zddc.IsAdminForChain(chain, p.Email, excludeLeaf)
in := AllowInput{Path: path, Action: action, PolicyChain: chainToSerializable(chain)}
in.User.Email = p.Email
in.User.IsActiveAdmin = isAdmin
return d.Allow(ctx, in)
}
// cachingDecider wraps another Decider with a small per-decision cache. // cachingDecider wraps another Decider with a small per-decision cache.
// Designed for the external-OPA hot path: a single .archive listing or // Designed for the external-OPA hot path: a single .archive listing or
// directory enumeration can hit the same (email, dir-policy) tuple // directory enumeration can hit the same (email, dir-policy) tuple