37 lines
1.7 KiB
Go
37 lines
1.7 KiB
Go
package handler
|
|
|
|
import (
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"codeberg.org/VARASYS/ZDDC/zddc/internal/apps"
|
|
"codeberg.org/VARASYS/ZDDC/zddc/internal/policy"
|
|
)
|
|
|
|
// configWriteAction returns the action a write to absPath must be authorized
|
|
// as. The .zddc cascade file and the .zddc.zip bundle are policy, not content:
|
|
// mutating either is a VerbA operation requiring standing config-edit authority
|
|
// (IsConfigEditor — a subtree admin or `a`-verb holder, no elevation), which
|
|
// the decider enforces when the action is tagged ActionAdmin. For any other
|
|
// path the supplied default action is returned unchanged.
|
|
//
|
|
// This is the single predicate behind the per-verb escalation that previously
|
|
// lived inlined in serveFilePut/serveFileDelete (.zddc only) and was MISSING
|
|
// from serveFileMove — letting a MOVE plant or relocate a policy file with mere
|
|
// create/write authority. PUT/DELETE on a URL-visible .zddc.zip are also
|
|
// existence-gated to config-editors at dispatch (the bundle visibility gate in
|
|
// cmd/zddc-server), but a MOVE destination rides in the X-ZDDC-Destination
|
|
// header and never reaches that gate — so the authority bar must be enforced
|
|
// here, on the resolved target path, for every write verb.
|
|
//
|
|
// Matching is case-insensitive to align with HasReservedSidecar: ZDDC_ROOT may
|
|
// sit on a case-insensitive filesystem (SMB/CIFS/Azure Files) where `.ZDDC` /
|
|
// `.ZDDC.ZIP` resolve to the same files, and a case-varied target must not slip
|
|
// past the gate.
|
|
func configWriteAction(absPath, def string) string {
|
|
base := filepath.Base(absPath)
|
|
if strings.EqualFold(base, ".zddc") || strings.EqualFold(base, apps.BundleName) {
|
|
return policy.ActionAdmin
|
|
}
|
|
return def
|
|
}
|