1package user 2 3import ( 4 "errors" 5 "fmt" 6 7 "github.com/AlecAivazis/survey" 8 "github.com/sensu/sensu-go/cli" 9 "github.com/sensu/sensu-go/cli/commands/flags" 10 "github.com/sensu/sensu-go/cli/commands/helpers" 11 "github.com/sensu/sensu-go/types" 12 "github.com/spf13/cobra" 13 "github.com/spf13/pflag" 14) 15 16var ( 17 errBadCurrentPassword = errors.New("given password did not match the one on file") 18 errEmptyCurrentPassword = errors.New("current user's password must be provided") 19 errPasswordsDoNotMatch = errors.New("given passwords do not match") 20) 21 22type passwordOpts struct { 23 New string `survey:"new-password"` 24 Confirm string `survey:"confirm-password"` 25} 26 27// SetPasswordCommand adds command that allows user to create new users 28func SetPasswordCommand(cli *cli.SensuCli) *cobra.Command { 29 cmd := &cobra.Command{ 30 Use: "change-password [USERNAME]", 31 Short: "change password for given user", 32 SilenceUsage: true, 33 PreRun: func(cmd *cobra.Command, args []string) { 34 isInteractive, _ := cmd.Flags().GetBool(flags.Interactive) 35 if !isInteractive { 36 // Mark flags are required for bash-completions 37 _ = cmd.MarkFlagRequired("new-password") 38 } 39 }, 40 RunE: func(cmd *cobra.Command, args []string) error { 41 if len(args) > 1 { 42 _ = cmd.Help() 43 return errors.New("invalid argument(s) received") 44 } 45 46 isInteractive, _ := cmd.Flags().GetBool(flags.Interactive) 47 48 password := &passwordOpts{} 49 var promptForCurrentPassword bool 50 var username string 51 52 // Retrieve current username from JWT 53 currentUsername := helpers.GetCurrentUsername(cli.Config) 54 55 // If no username is given we use the current user's name 56 if len(args) > 0 { 57 username = args[0] 58 59 // Prompt for password if specified username is current user 60 if username == currentUsername { 61 promptForCurrentPassword = true 62 } 63 } else { 64 username = currentUsername 65 promptForCurrentPassword = true 66 } 67 68 // As a precaution, ask for the current user's password 69 if promptForCurrentPassword { 70 if err := verifyExistingPassword(cli, cmd.Flags(), isInteractive, currentUsername); err != nil { 71 return err 72 } 73 } 74 75 if isInteractive { 76 // Prompt user for new password 77 if err := password.administerQuestionnaire(); err != nil { 78 return err 79 } 80 } else { 81 if err := password.withFlags(cmd.Flags()); err != nil { 82 _ = cmd.Help() 83 return errors.New("new password must be provided") 84 } 85 } 86 87 // Validate new password 88 if err := password.validate(); err != nil { 89 return err 90 } 91 92 // Update password 93 err := cli.Client.UpdatePassword(username, password.New) 94 if err != nil { 95 return err 96 } 97 98 fmt.Fprintln(cmd.OutOrStdout(), "Updated") 99 return nil 100 }, 101 } 102 103 _ = cmd.Flags().String("current-password", "", "current password") 104 _ = cmd.Flags().String("new-password", "", "new password") 105 106 helpers.AddInteractiveFlag(cmd.Flags()) 107 108 return cmd 109} 110 111func verifyExistingPassword(cli *cli.SensuCli, flags *pflag.FlagSet, isInteractive bool, username string) error { 112 input := struct{ Password string }{} 113 114 if isInteractive { 115 qs := []*survey.Question{ 116 { 117 Name: "password", 118 Prompt: &survey.Password{Message: "Current Password:\t"}, 119 Validate: survey.Required, 120 }, 121 } 122 123 // Get password 124 if err := survey.Ask(qs, &input); err != nil { 125 return err 126 } 127 } else { 128 input.Password, _ = flags.GetString("current-password") 129 } 130 131 // Validate that the current password has been provided 132 if input.Password == "" { 133 return errEmptyCurrentPassword 134 } 135 136 // Attempt to authenticate 137 if _, err := cli.Client.CreateAccessToken(cli.Config.APIUrl(), username, input.Password); err != nil { 138 return errBadCurrentPassword 139 } 140 141 return nil 142} 143 144func (opts *passwordOpts) administerQuestionnaire() error { 145 qs := []*survey.Question{ 146 { 147 Name: "new-password", 148 Prompt: &survey.Password{Message: "New Password:\t\t"}, 149 Validate: survey.Required, 150 }, 151 { 152 Name: "confirm-password", 153 Prompt: &survey.Password{Message: "Confirm Password:\t"}, 154 Validate: survey.Required, 155 }, 156 } 157 158 return survey.Ask(qs, opts) 159} 160 161func (opts *passwordOpts) withFlags(flags *pflag.FlagSet) error { 162 password, _ := flags.GetString("new-password") 163 if password == "" { 164 return errors.New("empty password") 165 } 166 167 opts.New = password 168 opts.Confirm = password 169 170 return nil 171} 172 173func (opts *passwordOpts) validate() error { 174 if opts.New != opts.Confirm { 175 return errPasswordsDoNotMatch 176 } 177 178 user := types.User{Password: opts.New} 179 return user.ValidatePassword() 180} 181