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:
parent
1c0777a847
commit
465d2f605c
1 changed files with 50 additions and 0 deletions
|
|
@ -73,6 +73,20 @@ import (
|
|||
type AllowInput struct {
|
||||
User struct {
|
||||
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"`
|
||||
Path string `json:"path"`
|
||||
Action string `json:"action,omitempty"`
|
||||
|
|
@ -219,6 +233,17 @@ func (d *InternalDecider) Allow(_ context.Context, input AllowInput) (bool, erro
|
|||
verb := actionVerb(input.Action)
|
||||
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
|
||||
// defaults.zddc.yaml — archive/<party>/received and issued carry
|
||||
// `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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Designed for the external-OPA hot path: a single .archive listing or
|
||||
// directory enumeration can hit the same (email, dir-policy) tuple
|
||||
|
|
|
|||
Loading…
Reference in a new issue