From 633945b1d109fe7eb393d016080bfd8220acf463 Mon Sep 17 00:00:00 2001 From: Mitchell Simon Date: Sat, 31 Aug 2019 17:19:25 -0400 Subject: [PATCH] Refactor error handling in prompt mode; add cred flag and reset func --- sp/commands/commands.go | 56 ++++++++++++++++++++++++++------------- sp/commands/create.go | 2 ++ sp/commands/delete.go | 6 +++-- sp/commands/get.go | 6 +++-- sp/commands/list.go | 4 ++- sp/commands/root.go | 24 ++++++++++++----- sp/commands/update.go | 6 +++-- sp/repositories/config.go | 1 + 8 files changed, 72 insertions(+), 33 deletions(-) diff --git a/sp/commands/commands.go b/sp/commands/commands.go index c311405..9be5ef1 100644 --- a/sp/commands/commands.go +++ b/sp/commands/commands.go @@ -19,9 +19,10 @@ var errSourceNotFound = errors.New("source host not found") type credentialFlagSet struct { includePasswordFlags bool - includeHostFlag bool + includeCredFlags bool sourceHost string + primary string noNumbers bool noSpecials bool length uint @@ -32,35 +33,47 @@ func (set credentialFlagSet) withPasswordFlags() credentialFlagSet { return set } -func (set credentialFlagSet) withHostFlag() credentialFlagSet { - set.includeHostFlag = true +func (set credentialFlagSet) withCredFlags() credentialFlagSet { + set.includeCredFlags = true return set } func (set *credentialFlagSet) register(cmd *cobra.Command) { - if set.includeHostFlag { + if set.includeCredFlags { cmd.Flags().StringVarP(&set.sourceHost, "source-host", "s", "", "filter results to this source host") + cmd.Flags().StringVarP(&set.primary, "primary", "p", "", "specify a primary user key (must include tag if applicable)") } if set.includePasswordFlags { cmd.Flags().BoolVarP(&set.noNumbers, "no-numbers", "n", false, "do not use numbers in the generated password") - cmd.Flags().BoolVarP(&set.noSpecials, "no-specials", "p", false, "do not use special characters in the generated password") + cmd.Flags().BoolVarP(&set.noSpecials, "no-specials", "e", false, "do not use special characters in the generated password") cmd.Flags().UintVarP(&set.length, "length", "l", 32, "length of the generated password") } } +func (set *credentialFlagSet) resetValues() { + set.sourceHost = "" + set.primary = "" + set.noNumbers = false + set.noSpecials = false + set.length = 0 +} + +var checkPromptMode = false + func check(err error) { if err != nil { - fmt.Println(err) + if checkPromptMode { + panic(err) + } + + fmt.Fprintln(os.Stdout, err) os.Exit(1) } } -func selectCredential(client types.CredentialsClient, sourceHost string) types.Credential { - var ( - idKey string - prompt survey.Prompt - ) +func selectCredential(client types.CredentialsClient, sourceHost string, primary string) types.Credential { + var prompt survey.Prompt ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() @@ -128,19 +141,24 @@ receive: keyIDMap[key] = md.ID } - prompt = &survey.Select{ - Message: "Primary user key (and tag):", - Options: keys, - PageSize: 20, - VimMode: true, - } + if primary == "" { + var idKey string + prompt = &survey.Select{ + Message: "Primary user key (and tag):", + Options: keys, + PageSize: 20, + VimMode: true, + } - check(survey.AskOne(prompt, &idKey, nil)) + check(survey.AskOne(prompt, &idKey, nil)) + + primary = idKey + } ctx, cancel = context.WithTimeout(context.Background(), time.Second*5) defer cancel() - cred, err := client.Get(ctx, keyIDMap[idKey]) + cred, err := client.Get(ctx, keyIDMap[primary]) check(err) return cred diff --git a/sp/commands/create.go b/sp/commands/create.go index 6722c41..cdf46ef 100644 --- a/sp/commands/create.go +++ b/sp/commands/create.go @@ -27,6 +27,8 @@ func makeCreate(repo clitypes.ConfigRepo, initClient credentialsClientInit) *cob password.`, Run: func(_ *cobra.Command, args []string) { + defer flags.resetValues() + var ( otp bool cleancb bool diff --git a/sp/commands/delete.go b/sp/commands/delete.go index b8c34b9..ce8e2cd 100644 --- a/sp/commands/delete.go +++ b/sp/commands/delete.go @@ -10,7 +10,7 @@ import ( ) func makeDelete(initClient credentialsClientInit) *cobra.Command { - flags := credentialFlagSet{}.withHostFlag() + flags := credentialFlagSet{}.withCredFlags() deleteCmd := &cobra.Command{ Use: "delete", @@ -18,12 +18,14 @@ func makeDelete(initClient credentialsClientInit) *cobra.Command { Long: `Delete a credential using the given ID, permanently. THERE IS NO UNDOING THIS ACTION.`, Run: func(cmd *cobra.Command, args []string) { + defer flags.resetValues() + ctx, cancel := context.WithTimeout(context.Background(), time.Second*60) defer cancel() client := initClient(ctx) - cred := selectCredential(client, flags.sourceHost) + cred := selectCredential(client, flags.sourceHost, flags.primary) fmt.Println(cred) diff --git a/sp/commands/get.go b/sp/commands/get.go index 8f616ee..21097c8 100644 --- a/sp/commands/get.go +++ b/sp/commands/get.go @@ -16,7 +16,7 @@ import ( ) func makeGet(repo clitypes.ConfigRepo, initClient credentialsClientInit) *cobra.Command { - flags := credentialFlagSet{}.withHostFlag() + flags := credentialFlagSet{}.withCredFlags() getCmd := &cobra.Command{ Use: "get", @@ -25,6 +25,8 @@ func makeGet(repo clitypes.ConfigRepo, initClient credentialsClientInit) *cobra. decrypting password.`, Run: func(cmd *cobra.Command, args []string) { + defer flags.resetValues() + var ( copyPass bool cleancb bool @@ -38,7 +40,7 @@ decrypting password.`, masterpass, cfg, err := repo.OpenConfig() check(err) - cred := selectCredential(client, flags.sourceHost) + cred := selectCredential(client, flags.sourceHost, flags.primary) fmt.Println(cred) diff --git a/sp/commands/list.go b/sp/commands/list.go index b4fe308..afc7e08 100644 --- a/sp/commands/list.go +++ b/sp/commands/list.go @@ -12,7 +12,7 @@ import ( ) func makeList(initClient credentialsClientInit) *cobra.Command { - flags := credentialFlagSet{}.withHostFlag() + flags := credentialFlagSet{}.withCredFlags() listCmd := &cobra.Command{ Use: "list", @@ -21,6 +21,8 @@ func makeList(initClient credentialsClientInit) *cobra.Command { includes almost all the information but the most sensitive.`, Run: func(cmd *cobra.Command, args []string) { + defer flags.resetValues() + ctx, cancel := context.WithTimeout(context.Background(), time.Second*60) defer cancel() diff --git a/sp/commands/root.go b/sp/commands/root.go index 2c0c824..1829b11 100644 --- a/sp/commands/root.go +++ b/sp/commands/root.go @@ -19,10 +19,13 @@ import ( func Execute() { rootCmd := &cobra.Command{ Use: "sp", - Run: run, + Run: runPromptMode, Short: "This is the CLI client for Selfpass.", Long: `This is the CLI client for Selfpass, the self-hosted password manager. With this tool you -can interact with the entire Selfpass API.`, +can interact with the entire Selfpass API. + +When run without a command specified sp enters prompt mode. All commands and flags are the same, +but your master pass only need be entered once until you exit the prompt.`, Version: "v0.1.0", } @@ -47,7 +50,7 @@ can interact with the entire Selfpass API.`, check(rootCmd.Execute()) } -func run(cmd *cobra.Command, _ []string) { +func runPromptMode(cmd *cobra.Command, _ []string) { ss := []prompt.Suggest{ {Text: "exit", Description: "Exit selfpass prompt"}, } @@ -67,6 +70,8 @@ func run(cmd *cobra.Command, _ []string) { return prompt.FilterHasPrefix(ss, d.TextBeforeCursor(), true) } + checkPromptMode = true + executor := func(argstr string) { args := strings.Split(argstr, " ") @@ -75,11 +80,15 @@ func run(cmd *cobra.Command, _ []string) { os.Exit(0) } + defer func() { + if err := recover(); err != nil { + fmt.Fprint(os.Stderr, err, "\n\n") + } + }() + cmd.SetArgs(args) - err := cmd.Execute() - if err != nil { - fmt.Println(err) - } + cmd.Execute() + fmt.Println() } @@ -98,6 +107,7 @@ func run(cmd *cobra.Command, _ []string) { prompt.OptionPreviewSuggestionTextColor(prompt.Red), ) + fmt.Println("\nWelcome to the selfpass prompt.") p.Run() } diff --git a/sp/commands/update.go b/sp/commands/update.go index ad874bf..c9940ca 100644 --- a/sp/commands/update.go +++ b/sp/commands/update.go @@ -18,7 +18,7 @@ import ( ) func makeUpdate(repo clitypes.ConfigRepo, initClient credentialsClientInit) *cobra.Command { - flags := credentialFlagSet{}.withHostFlag().withPasswordFlags() + flags := credentialFlagSet{}.withCredFlags().withPasswordFlags() updateCmd := &cobra.Command{ Use: "update", @@ -27,6 +27,8 @@ func makeUpdate(repo clitypes.ConfigRepo, initClient credentialsClientInit) *cob password.`, Run: func(_ *cobra.Command, args []string) { + defer flags.resetValues() + var ( newpass bool otp bool @@ -43,7 +45,7 @@ password.`, client := initClient(ctx) - cred := selectCredential(client, flags.sourceHost) + cred := selectCredential(client, flags.sourceHost, flags.primary) mdqs := []*survey.Question{ { diff --git a/sp/repositories/config.go b/sp/repositories/config.go index c873925..885d77f 100644 --- a/sp/repositories/config.go +++ b/sp/repositories/config.go @@ -71,6 +71,7 @@ func (mgr *ConfigManager) OpenConfig() (output string, v *viper.Viper, err error if err == errConfigDecrypted { configDecrypted = true } else if err != nil && err.Error() == crypto.ErrAuthenticationFailed.Error() { + mgr.masterpass = "" return output, nil, errors.New("incorrect masterpass") } else if err != nil { return output, nil, err