Three improvements bundled because they all ship as zddc-server v0.0.2: * /.admin/ debug dashboard with /whoami, /config, /logs sub-routes. Authorization via a top-level `admins:` glob list in <ZDDC_ROOT>/.zddc (root-only — subdir entries deliberately ignored to prevent privilege escalation via subtree write access). Non-admin requests get 404 so the page is invisible. Recent logs surface via a 500-entry slog ring buffer teed off the existing TextHandler. Lets operators debug without kubectl exec. * Default ZDDC_EMAIL_HEADER changes from `X-Email` to `X-Auth-Request-Email` — the oauth2-proxy / nginx auth-request convention that the TND helm chart already sets explicitly. Operators who set the env var explicitly are unaffected; deployments relying on the previous default need to set ZDDC_EMAIL_HEADER=X-Email or update their proxy. * dispatch() rejects any URL whose segments contain a dot prefix other than the recognized virtual prefixes (.admin, cfg.IndexPath / .archive). Matches the existing listing-pipeline filter so hidden subtrees on the served PVC (e.g. /srv/.devshell — used by the in-cluster dev-shell for persistent home-dir state) become unreachable via direct HTTP fetch, not just hidden in listings. Refreshes the X-Email reference in website/index.html accordingly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
108 lines
2.9 KiB
Go
108 lines
2.9 KiB
Go
package zddc
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestIsAdmin(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
zddcBody string // contents of <root>/.zddc; empty string means no file
|
|
email string
|
|
want bool
|
|
}{
|
|
{
|
|
name: "no zddc file → not admin",
|
|
zddcBody: "",
|
|
email: "alice@example.com",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "zddc file with no admins key → not admin",
|
|
zddcBody: "acl:\n allow: [\"*\"]\n",
|
|
email: "alice@example.com",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "zddc file with empty admins list → not admin",
|
|
zddcBody: "admins: []\n",
|
|
email: "alice@example.com",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "exact-match admin → admin",
|
|
zddcBody: "admins:\n - alice@example.com\n",
|
|
email: "alice@example.com",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "domain glob admin → admin",
|
|
zddcBody: "admins:\n - \"*@example.com\"\n",
|
|
email: "alice@example.com",
|
|
want: true,
|
|
},
|
|
{
|
|
name: "domain glob admin, wrong domain → not admin",
|
|
zddcBody: "admins:\n - \"*@example.com\"\n",
|
|
email: "alice@other.org",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "non-matching listed admin → not admin",
|
|
zddcBody: "admins:\n - bob@example.com\n",
|
|
email: "alice@example.com",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "empty email never matches even if pattern is *",
|
|
zddcBody: "admins:\n - \"*\"\n",
|
|
email: "",
|
|
want: false,
|
|
},
|
|
{
|
|
name: "acl deny does not affect admins",
|
|
zddcBody: "acl:\n deny: [\"*@example.com\"]\nadmins:\n - alice@example.com\n",
|
|
email: "alice@example.com",
|
|
want: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
root := t.TempDir()
|
|
if tc.zddcBody != "" {
|
|
if err := os.WriteFile(filepath.Join(root, ".zddc"), []byte(tc.zddcBody), 0o644); err != nil {
|
|
t.Fatalf("write .zddc: %v", err)
|
|
}
|
|
}
|
|
if got := IsAdmin(root, tc.email); got != tc.want {
|
|
t.Errorf("IsAdmin(%q, %q) = %v, want %v", root, tc.email, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestIsAdminSubdirIgnored documents that admins entries in subdirectory
|
|
// .zddc files are NOT honored — only the root .zddc grants admin. Otherwise
|
|
// anyone with subtree write access could elevate themselves.
|
|
func TestIsAdminSubdirIgnored(t *testing.T) {
|
|
root := t.TempDir()
|
|
sub := filepath.Join(root, "project")
|
|
if err := os.MkdirAll(sub, 0o755); err != nil {
|
|
t.Fatalf("mkdir: %v", err)
|
|
}
|
|
|
|
// Root has no admins; subdir tries to grant admin.
|
|
if err := os.WriteFile(filepath.Join(root, ".zddc"), []byte("acl:\n allow: [\"*\"]\n"), 0o644); err != nil {
|
|
t.Fatalf("write root .zddc: %v", err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(sub, ".zddc"), []byte("admins:\n - mallory@example.com\n"), 0o644); err != nil {
|
|
t.Fatalf("write subdir .zddc: %v", err)
|
|
}
|
|
|
|
if IsAdmin(root, "mallory@example.com") {
|
|
t.Error("subdir .zddc admins entry was honored — that is a privilege-escalation hole")
|
|
}
|
|
}
|