refactor(zddc): extract writeAutoOwnZddc into zddc.WriteAutoOwnZddc

Pure refactor. The mkdir post-hook in handler/fileapi.go duplicated
zddc-package types; lifting the body into the package itself lets the
upcoming EnsureCanonicalAncestors helper share it without re-exposing
the file API's internals.

No behaviour change. The grant shape (creator email → rwcda + CreatedBy
audit field) and the atomic-write path through zddc.WriteFile are
unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZDDC 2026-05-07 08:20:34 -05:00
parent 5fa5d13b10
commit a471de8788
2 changed files with 23 additions and 14 deletions

View file

@ -526,7 +526,7 @@ func serveFileMkdir(cfg config.Config, w http.ResponseWriter, r *http.Request) {
if email := EmailFromContext(r); email != "" {
parentName := filepath.Base(filepath.Dir(abs))
if zddc.IsAutoOwnParent(parentName) {
if err := writeAutoOwnZddc(abs, email); err != nil {
if err := zddc.WriteAutoOwnZddc(abs, email); err != nil {
slog.Warn("auto-own .zddc write failed", "path", abs, "err", err)
}
}
@ -537,19 +537,6 @@ func serveFileMkdir(cfg config.Config, w http.ResponseWriter, r *http.Request) {
auditFile(r, "mkdir", cleanURL, http.StatusCreated, 0, nil)
}
// writeAutoOwnZddc serializes a creator-grant .zddc into newDir.
// Marshals via the same yaml encoder ParseFile reads (round-trip
// guaranteed) and writes atomically via zddc.WriteAtomic.
func writeAutoOwnZddc(newDir, email string) error {
zf := zddc.ZddcFile{
ACL: zddc.ACLRules{
Permissions: map[string]string{email: "rwcda"},
},
CreatedBy: email,
}
return zddc.WriteFile(newDir, zf)
}
// auditFile emits a structured log line for each file API operation.
// AccessLogMiddleware already logs every request — this adds an
// op-tagged line so audit consumers can filter by operation without

View file

@ -71,6 +71,28 @@ var AutoOwnFolderNames = []string{"Incoming", "Working", "Staging"}
// Deprecated: use PartyFolders + IsWormPath.
var WormFolderNames = []string{"Issued", "Received"}
// WriteAutoOwnZddc serialises a creator-grant .zddc into dir, granting
// principalEmail rwcda and recording it in CreatedBy. Used by the file
// API's mkdir post-hook (and by EnsureCanonicalAncestors) to seed
// ownership when a new auto-own folder is materialised.
//
// The grant is identical to what an operator would write by hand —
// direct email pattern, "rwcda" verb set — so the creator can later
// edit the file normally to add collaborators.
//
// Atomic: marshals via the same yaml encoder ParseFile reads
// (round-trip guaranteed) and writes via zddc.WriteFile (which
// performs an atomic temp-write + rename via zddc.WriteAtomic).
func WriteAutoOwnZddc(dir, principalEmail string) error {
zf := ZddcFile{
ACL: ACLRules{
Permissions: map[string]string{principalEmail: "rwcda"},
},
CreatedBy: principalEmail,
}
return WriteFile(dir, zf)
}
// ResolveCanonical returns the on-disk name of the canonical folder
// 'logical' (lowercase) inside parentDir, or "" if no case variant
// exists. Caller decides whether to MkdirAll(parentDir+"/"+logical)