package config import ( "os" "path/filepath" "strings" "testing" "nadir/internal/module" "nadir/internal/rbac" "github.com/danielgtaylor/huma/v2" ) // fakeModule implements module.Module with a fixed permission set, so config // tests don't depend on the concrete modules' exec behavior. type fakeModule struct { id string perms []rbac.Permission } func (f fakeModule) ID() string { return f.id } func (f fakeModule) Permissions() []rbac.Permission { return f.perms } func (f fakeModule) Register(huma.API) {} func mods() []module.Module { return []module.Module{ fakeModule{id: "system", perms: []rbac.Permission{rbac.Read, rbac.Write, rbac.Root}}, fakeModule{id: "services", perms: []rbac.Permission{rbac.Read, rbac.Write}}, } } func TestSecureCookieDefaultsFalse(t *testing.T) { if (&File{}).SecureCookie() { t.Error("omitted secure_tls should default to false") } yes := true if !(&File{Server: Server{SecureTLS: &yes}}).SecureCookie() { t.Error("secure_tls: true should enable the Secure flag") } } func TestLoad(t *testing.T) { path := filepath.Join(t.TempDir(), "config.yaml") os.WriteFile(path, []byte(` server: secure_tls: false roles: admin: "*": ["*"] assignments: urania: [admin] `), 0600) f, err := Load(path) if err != nil { t.Fatal(err) } if f.SecureCookie() { t.Error("secure_tls: false not parsed") } if len(f.Roles["admin"]) == 0 || len(f.Assignments["urania"]) == 0 { t.Error("roles/assignments not parsed") } } func TestLoadMissingFile(t *testing.T) { if _, err := Load(filepath.Join(t.TempDir(), "nope.yaml")); err == nil { t.Fatal("expected error for missing file") } } func TestApplyValid(t *testing.T) { f := &File{ Roles: map[string]map[string][]string{ "admin": {"*": {"*"}}, "auditor": {"*": {"read"}}, "sysop": {"system": {"read", "root"}}, }, Assignments: map[string][]string{"urania": {"admin"}, "bob": {"sysop"}}, } roles := rbac.New() if err := Apply(f, roles, mods()); err != nil { t.Fatal(err) } if !roles.Can("urania", "services", rbac.Write) { t.Error("admin wildcard not applied") } if !roles.Can("bob", "system", rbac.Root) || roles.Can("bob", "system", rbac.Write) { t.Error("sysop grants not applied correctly") } } func TestApplyErrors(t *testing.T) { tests := []struct { name string f *File }{ {"unknown module", &File{Roles: map[string]map[string][]string{ "r": {"firewall": {"read"}}}}}, {"unknown perm on known module", &File{Roles: map[string]map[string][]string{ "r": {"system": {"banana"}}}}}, {"wildcard module with perm no module exports", &File{Roles: map[string]map[string][]string{ "r": {"*": {"banana"}}}}}, {"assignment to unknown role", &File{ Roles: map[string]map[string][]string{"r": {"system": {"read"}}}, Assignments: map[string][]string{"u": {"ghost"}}}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if err := Apply(tt.f, rbac.New(), mods()); err == nil { t.Errorf("expected error for %s", tt.name) } }) } } func TestApplyWildcardPermAlwaysOK(t *testing.T) { f := &File{Roles: map[string]map[string][]string{"r": {"system": {"*"}}}} if err := Apply(f, rbac.New(), mods()); err != nil { t.Fatalf("wildcard permission should validate: %v", err) } } func TestDefaultPathAndExpandPath(t *testing.T) { defaultPath, err := DefaultPath() if err != nil { t.Fatalf("DefaultPath failed: %v", err) } expectedSuffix := filepath.Join("nadir", "config.yaml") if !filepath.IsAbs(defaultPath) || !strings.HasSuffix(defaultPath, expectedSuffix) { t.Errorf("expected default path to end with %q and be absolute, got %q", expectedSuffix, defaultPath) } expanded, err := ExpandPath("~/foo/config.yaml") if err != nil { t.Fatalf("ExpandPath failed: %v", err) } if !filepath.IsAbs(expanded) || !strings.HasSuffix(expanded, filepath.Join("foo", "config.yaml")) { t.Errorf("ExpandPath did not resolve ~/ correctly, got %q", expanded) } plain := "/etc/nadir/config.yaml" expandedPlain, err := ExpandPath(plain) if err != nil { t.Fatalf("ExpandPath failed for plain path: %v", err) } if expandedPlain != plain { t.Errorf("expected no-op for %q, got %q", plain, expandedPlain) } }