diff options
| author | Paul Buetow <paul@buetow.org> | 2026-04-17 08:45:00 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-04-17 08:45:00 +0300 |
| commit | e4671dc15d224d944fe5c3715c95793c21167443 (patch) | |
| tree | e63b9a11d24839b2780ce1893bcd7e75f5f0cd9f | |
| parent | 426b9531c32d1afb154feea6e9818d59ff73d696 (diff) | |
feat: add backend config fields for KeePass support (task h4)
Add Backend (default 'geheim'), KDBXPath, KDBXKeyFile, KDBXPassFile
fields to Config. Defaults mirror migrate_kdbx.go hardcoded values.
Extend expandPathFieldsWithHome for the three path fields. Table-driven
tests cover defaults, JSON override, and tilde expansion.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| -rw-r--r-- | internal/config/config.go | 29 | ||||
| -rw-r--r-- | internal/config/config_test.go | 70 |
2 files changed, 99 insertions, 0 deletions
diff --git a/internal/config/config.go b/internal/config/config.go index b8d0532..3b4bed4 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -22,6 +22,8 @@ const ( // Config holds all application-wide configuration values. // JSON field names use snake_case to match the original geheim.rb Config::DEFAULTS keys. +// The Backend field selects the storage backend: "geheim" (default, encrypted +// .index/.data files in a git repo) or "keepass" (a .kdbx database file). type Config struct { DataDir string `json:"data_dir"` ExportDir string `json:"export_dir"` @@ -33,6 +35,19 @@ type Config struct { GnomeClipboardCmd string `json:"gnome_clipboard_cmd"` MacOSClipboardCmd string `json:"macos_clipboard_cmd"` SyncRepos []string `json:"sync_repos"` + + // Backend selects the storage backend: "geheim" (default) or "keepass". + Backend string `json:"backend"` + // KDBXPath is the path to the KeePass .kdbx database file. + // Defaults to ~/Documents/Keepass/master to match migrate-kdbx defaults. + KDBXPath string `json:"kdbx_path"` + // KDBXKeyFile is the optional path to a KeePass key file. + // An empty value disables key file authentication. + KDBXKeyFile string `json:"kdbx_key_file"` + // KDBXPassFile is the optional path to a file containing the KeePass password. + // Defaults to ~/.master.pass to match migrate-kdbx defaults. + // When empty, the password is read interactively at startup. + KDBXPassFile string `json:"kdbx_pass_file"` } // resolveHomeDir resolves the current user's home directory from OS state. @@ -99,6 +114,15 @@ func defaultConfigWithHome(home string) Config { GnomeClipboardCmd: "gpaste-client", MacOSClipboardCmd: "pbcopy", SyncRepos: []string{"git1", "git2"}, + + // Backend defaults match the geheim (original) storage backend. + // KDBXPath and KDBXPassFile defaults mirror the migrate-kdbx command + // at internal/cli/migrate_kdbx.go so users who already use that command + // have zero additional configuration to provide. + Backend: "geheim", + KDBXPath: filepath.Join(home, "Documents", "Keepass", "master"), + KDBXKeyFile: "", + KDBXPassFile: filepath.Join(home, ".master.pass"), } } @@ -126,10 +150,15 @@ func expandTilde(path string) string { } // expandPathFieldsWithHome tilde-expands every path-typed field in cfg in place. +// This covers both the geheim fields (DataDir, ExportDir, KeyFile) and the +// KeePass fields (KDBXPath, KDBXKeyFile, KDBXPassFile). func expandPathFieldsWithHome(cfg *Config, home string) { cfg.DataDir = expandTildeWithHome(cfg.DataDir, home) cfg.ExportDir = expandTildeWithHome(cfg.ExportDir, home) cfg.KeyFile = expandTildeWithHome(cfg.KeyFile, home) + cfg.KDBXPath = expandTildeWithHome(cfg.KDBXPath, home) + cfg.KDBXKeyFile = expandTildeWithHome(cfg.KDBXKeyFile, home) + cfg.KDBXPassFile = expandTildeWithHome(cfg.KDBXPassFile, home) } // expandPathFields tilde-expands every path-typed field in cfg in place. diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 4b2e7d9..7ffcc68 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -286,6 +286,76 @@ func TestLoad_missing_file_no_warning(t *testing.T) { } } +// TestLoad_backendDefaults verifies that the backend-related fields have the +// expected default values (matching migrate-kdbx defaults) when no config file +// exists. +func TestLoad_backendDefaults(t *testing.T) { + dir := t.TempDir() + t.Setenv("HOME", dir) + + cfg := Load() + + cases := []struct{ name, got, want string }{ + {"Backend", cfg.Backend, "geheim"}, + {"KDBXPath", cfg.KDBXPath, filepath.Join(dir, "Documents", "Keepass", "master")}, + {"KDBXKeyFile", cfg.KDBXKeyFile, ""}, + {"KDBXPassFile", cfg.KDBXPassFile, filepath.Join(dir, ".master.pass")}, + } + for _, tc := range cases { + if tc.got != tc.want { + t.Errorf("%s = %q; want %q", tc.name, tc.got, tc.want) + } + } +} + +// TestLoad_backendJSONOverride verifies that backend config fields can be +// overridden via the JSON config file and that absent fields keep their defaults. +func TestLoad_backendJSONOverride(t *testing.T) { + dir := t.TempDir() + t.Setenv("HOME", dir) + writeUserConfig(t, dir, `{"backend":"keepass","kdbx_path":"~/vaults/work.kdbx","kdbx_key_file":"~/vaults/work.key"}`) + + cfg := Load() + + cases := []struct{ name, got, want string }{ + {"Backend", cfg.Backend, "keepass"}, + {"KDBXPath", cfg.KDBXPath, filepath.Join(dir, "vaults", "work.kdbx")}, + {"KDBXKeyFile", cfg.KDBXKeyFile, filepath.Join(dir, "vaults", "work.key")}, + // KDBXPassFile not in JSON — must keep expanded default. + {"KDBXPassFile", cfg.KDBXPassFile, filepath.Join(dir, ".master.pass")}, + } + for _, tc := range cases { + if tc.got != tc.want { + t.Errorf("%s = %q; want %q", tc.name, tc.got, tc.want) + } + } +} + +// TestLoad_backendTildeExpansion verifies that tilde paths for all three KDBX +// path fields are expanded to absolute paths after Load(). +func TestLoad_backendTildeExpansion(t *testing.T) { + dir := t.TempDir() + t.Setenv("HOME", dir) + writeUserConfig(t, dir, `{ + "kdbx_path": "~/kp/db.kdbx", + "kdbx_key_file": "~/kp/db.key", + "kdbx_pass_file": "~/kp/db.pass" + }`) + + cfg := Load() + + cases := []struct{ name, got, want string }{ + {"KDBXPath", cfg.KDBXPath, filepath.Join(dir, "kp", "db.kdbx")}, + {"KDBXKeyFile", cfg.KDBXKeyFile, filepath.Join(dir, "kp", "db.key")}, + {"KDBXPassFile", cfg.KDBXPassFile, filepath.Join(dir, "kp", "db.pass")}, + } + for _, tc := range cases { + if tc.got != tc.want { + t.Errorf("%s = %q; want %q", tc.name, tc.got, tc.want) + } + } +} + // TestLoad_unreadable_file verifies that a config file that exists but cannot // be read emits a warning and returns defaults (the !os.IsNotExist branch). // EDITOR is unset so EditCmd falls back to "vi". |
