Files
vault-tui/internal/auth/prefill.go
T
f.weber ae30ba1240 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.
2026-08-14 11:09:03 +02:00

101 lines
2.6 KiB
Go

package auth
import (
"fmt"
"os"
"strings"
)
// EnvAuthParamPrefix + strings.ToUpper(Field.Name) is consulted by Prefill
// alongside each Field's own EnvFallback list, so any field can be
// overridden from the environment even if the method author didn't think
// to name a specific var for it.
const EnvAuthParamPrefix = "VAULT_TUI_AUTH_"
// PrefillSource is everything Prefill draws from, ordered highest priority
// first: CLIArgs > env fallback / VAULT_TUI_AUTH_* > ConfigArgs > Field.Default.
type PrefillSource struct {
CLIArgs map[string]string // `vault-tui login -method=x role=eng`
ConfigArgs map[string]string // profile.auth.params
LookupEnv func(string) (string, bool)
}
func (s PrefillSource) lookupEnv(key string) (string, bool) {
if s.LookupEnv != nil {
return s.LookupEnv(key)
}
return os.LookupEnv(key)
}
// Prefill resolves each of m's fields' initial value from src, in
// precedence order: CLIArgs > Field.EnvFallback > VAULT_TUI_AUTH_<NAME> >
// ConfigArgs > Field.Default.
func Prefill(m Method, src PrefillSource) Credentials {
out := Credentials{}
for _, f := range m.Fields() {
key := f.ConfigKey
if key == "" {
key = f.Name
}
if v, ok := src.CLIArgs[f.Name]; ok && v != "" {
out[f.Name] = v
continue
}
found := false
for _, envKey := range f.EnvFallback {
if v, ok := src.lookupEnv(envKey); ok && v != "" {
out[f.Name] = v
found = true
break
}
}
if found {
continue
}
if v, ok := src.lookupEnv(EnvAuthParamPrefix + strings.ToUpper(f.Name)); ok && v != "" {
out[f.Name] = v
continue
}
if v, ok := src.ConfigArgs[key]; ok && v != "" {
out[f.Name] = v
continue
}
if f.Default != "" {
out[f.Name] = f.Default
}
}
return out
}
// Missing returns the Required fields that are still empty after Prefill.
// The TUI renders a form for exactly these; a headless, TTY-attached CLI
// prompts for them; a headless, non-TTY CLI should treat a non-empty result
// as a hard error (never block on stdin).
func Missing(m Method, creds Credentials) []Field {
var out []Field
for _, f := range m.Fields() {
if f.Required && !creds.Has(f.Name) {
out = append(out, f)
}
}
return out
}
// Validate runs every Field.Validate and checks Required, returning the
// first error encountered.
func Validate(m Method, creds Credentials) error {
for _, f := range m.Fields() {
v, ok := creds[f.Name]
if f.Required && (!ok || v == "") {
return fmt.Errorf("missing required field %q", f.Name)
}
if ok && f.Validate != nil {
if err := f.Validate(v); err != nil {
return fmt.Errorf("field %q: %w", f.Name, err)
}
}
}
return nil
}