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" }