feat(vault-tui): implement KV client and service for managing secrets
- Added internal/vault/client.go for creating a Vault client with configuration settings. - Introduced internal/vault/errors.go to classify Vault API errors for better UI handling. - Created internal/vault/kv.go to manage KV secrets, including listing, reading, writing, and deleting operations. - Implemented internal/vault/mounts.go to list and describe secret engine mounts. - Developed internal/vault/service.go to provide a unified entry point for Vault operations. - Added internal/vault/kv_test.go for comprehensive testing of KV operations. - Introduced internal/ui/toast.go for transient notifications in the UI. - Added renovate.json for dependency management and updates.
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.morlana.online/f.weber/vault-tui/internal/auth"
|
||||
"git.morlana.online/f.weber/vault-tui/internal/config"
|
||||
)
|
||||
|
||||
// runWizard interactively builds one profile and writes it to path. It is
|
||||
// deliberately generic — it prompts for everything (address, namespace,
|
||||
// TLS, auth method and that method's fields) rather than assuming any
|
||||
// particular Vault deployment, per this project's design goal of not
|
||||
// hard-coding any environment's specifics into the tool itself.
|
||||
func runWizard(path string) (*config.File, error) {
|
||||
r := bufio.NewReader(os.Stdin)
|
||||
fmt.Fprintln(os.Stderr, "No config found — let's set up a Vault profile (you can edit it later at "+path+").")
|
||||
|
||||
profileName := ask(r, "Profile name", "default")
|
||||
addrDefault := "https://127.0.0.1:8200"
|
||||
if v, ok := config.OSEnviron(config.EnvAddress); ok && v != "" {
|
||||
addrDefault = v
|
||||
}
|
||||
address := ask(r, "Vault address", addrDefault)
|
||||
namespace := ask(r, "Namespace (blank for none)", "")
|
||||
|
||||
prof := &config.Profile{Address: address, Namespace: namespace}
|
||||
|
||||
if askYesNo(r, "Configure custom TLS (CA cert / client cert)?", false) {
|
||||
prof.TLS.CACert = ask(r, "CA certificate path (blank to use system trust)", "")
|
||||
prof.TLS.ClientCert = ask(r, "Client certificate path (blank for none)", "")
|
||||
if prof.TLS.ClientCert != "" {
|
||||
prof.TLS.ClientKey = ask(r, "Client key path", "")
|
||||
}
|
||||
if askYesNo(r, "Skip TLS verification (insecure, testing only)?", false) {
|
||||
t := true
|
||||
prof.TLS.SkipVerify = &t
|
||||
}
|
||||
}
|
||||
|
||||
names := auth.Default().Names()
|
||||
fmt.Fprintln(os.Stderr, "Available auth methods: "+strings.Join(names, ", "))
|
||||
method := ask(r, "Auth method", "oidc")
|
||||
m, ok := auth.Default().Get(method)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown auth method %q", method)
|
||||
}
|
||||
prof.Auth.Method = method
|
||||
mount := ask(r, "Auth mount path", m.DefaultMount())
|
||||
prof.Auth.Mount = mount
|
||||
|
||||
params := map[string]string{}
|
||||
for _, f := range m.Fields() {
|
||||
if f.Kind == auth.FieldSecret {
|
||||
// Secrets are never written to the config file; the user supplies
|
||||
// them at login time (env var, flag, or interactive prompt).
|
||||
continue
|
||||
}
|
||||
v := ask(r, f.Label, f.Default)
|
||||
if v != "" {
|
||||
params[f.Name] = v
|
||||
}
|
||||
}
|
||||
if len(params) > 0 {
|
||||
prof.Auth.Params = params
|
||||
}
|
||||
|
||||
file := &config.File{
|
||||
Version: config.SchemaVersion,
|
||||
CurrentProfile: profileName,
|
||||
Profiles: map[string]*config.Profile{profileName: prof},
|
||||
}
|
||||
if err := config.Save(path, file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "Saved "+path)
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func ask(r *bufio.Reader, label, def string) string {
|
||||
if def != "" {
|
||||
fmt.Fprintf(os.Stderr, "%s [%s]: ", label, def)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "%s: ", label)
|
||||
}
|
||||
line, _ := r.ReadString('\n')
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
return def
|
||||
}
|
||||
return line
|
||||
}
|
||||
|
||||
func askYesNo(r *bufio.Reader, label string, def bool) bool {
|
||||
d := "y/N"
|
||||
if def {
|
||||
d = "Y/n"
|
||||
}
|
||||
line := strings.ToLower(ask(r, label+" ("+d+")", ""))
|
||||
if line == "" {
|
||||
return def
|
||||
}
|
||||
return line == "y" || line == "yes"
|
||||
}
|
||||
Reference in New Issue
Block a user