github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/cmd/juju/user/change_password.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package user 5 6 import ( 7 "fmt" 8 9 "github.com/juju/cmd" 10 "github.com/juju/errors" 11 "launchpad.net/gnuflag" 12 13 "github.com/juju/juju/cmd/juju/block" 14 "github.com/juju/juju/environs/configstore" 15 ) 16 17 const userChangePasswordDoc = ` 18 Change the password for the user you are currently logged in as, 19 or as an admin, change the password for another user. 20 21 Examples: 22 # You will be prompted to enter a password. 23 juju user change-password 24 25 # Change the password to a random strong password. 26 juju user change-password --generate 27 28 # Change the password for bob 29 juju user change-password bob --generate 30 31 ` 32 33 // ChangePasswordCommand changes the password for a user. 34 type ChangePasswordCommand struct { 35 UserCommandBase 36 Password string 37 Generate bool 38 OutPath string 39 User string 40 } 41 42 // Info implements Command.Info. 43 func (c *ChangePasswordCommand) Info() *cmd.Info { 44 return &cmd.Info{ 45 Name: "change-password", 46 Args: "[username]", 47 Purpose: "changes the password for a user", 48 Doc: userChangePasswordDoc, 49 } 50 } 51 52 // SetFlags implements Command.SetFlags. 53 func (c *ChangePasswordCommand) SetFlags(f *gnuflag.FlagSet) { 54 f.BoolVar(&c.Generate, "generate", false, "generate a new strong password") 55 f.StringVar(&c.OutPath, "o", "", "specifies the path of the generated user environment file") 56 f.StringVar(&c.OutPath, "output", "", "") 57 } 58 59 // Init implements Command.Init. 60 func (c *ChangePasswordCommand) Init(args []string) error { 61 var err error 62 c.User, err = cmd.ZeroOrOneArgs(args) 63 if c.User == "" && c.OutPath != "" { 64 return errors.New("output is only a valid option when changing another user's password") 65 } 66 return err 67 } 68 69 // ChangePasswordAPI defines the usermanager API methods that the change 70 // password command uses. 71 type ChangePasswordAPI interface { 72 SetPassword(username, password string) error 73 Close() error 74 } 75 76 // EnvironInfoCredsWriter defines methods of the configstore API info that 77 // are used to change the password. 78 type EnvironInfoCredsWriter interface { 79 Write() error 80 SetAPICredentials(creds configstore.APICredentials) 81 Location() string 82 } 83 84 func (c *ChangePasswordCommand) getChangePasswordAPI() (ChangePasswordAPI, error) { 85 return c.NewUserManagerClient() 86 } 87 88 func (c *ChangePasswordCommand) getEnvironInfoWriter() (EnvironInfoCredsWriter, error) { 89 return c.ConnectionWriter() 90 } 91 92 func (c *ChangePasswordCommand) getConnectionCredentials() (configstore.APICredentials, error) { 93 return c.ConnectionCredentials() 94 } 95 96 var ( 97 getChangePasswordAPI = (*ChangePasswordCommand).getChangePasswordAPI 98 getEnvironInfoWriter = (*ChangePasswordCommand).getEnvironInfoWriter 99 getConnectionCredentials = (*ChangePasswordCommand).getConnectionCredentials 100 ) 101 102 // Run implements Command.Run. 103 func (c *ChangePasswordCommand) Run(ctx *cmd.Context) error { 104 var err error 105 106 c.Password, err = c.generateOrReadPassword(ctx, c.Generate) 107 if err != nil { 108 return errors.Trace(err) 109 } 110 111 var credsWriter EnvironInfoCredsWriter 112 var creds configstore.APICredentials 113 114 if c.User == "" { 115 // We get the creds writer before changing the password just to 116 // minimise the things that could go wrong after changing the password 117 // in the server. 118 credsWriter, err = getEnvironInfoWriter(c) 119 if err != nil { 120 return errors.Trace(err) 121 } 122 123 creds, err = getConnectionCredentials(c) 124 if err != nil { 125 return errors.Trace(err) 126 } 127 } else { 128 creds.User = c.User 129 } 130 131 client, err := getChangePasswordAPI(c) 132 if err != nil { 133 return err 134 } 135 defer client.Close() 136 137 oldPassword := creds.Password 138 creds.Password = c.Password 139 err = client.SetPassword(creds.User, c.Password) 140 if err != nil { 141 return block.ProcessBlockedError(err, block.BlockChange) 142 } 143 144 if c.User != "" { 145 return c.writeEnvironmentFile(ctx) 146 } 147 148 credsWriter.SetAPICredentials(creds) 149 if err := credsWriter.Write(); err != nil { 150 logger.Errorf("updating the environments file failed, reverting to original password") 151 setErr := client.SetPassword(creds.User, oldPassword) 152 if setErr != nil { 153 logger.Errorf("failed to set password back, you will need to edit your environments file by hand to specify the password: %q", c.Password) 154 return errors.Annotate(setErr, "failed to set password back") 155 } 156 return errors.Annotate(err, "failed to write new password to environments file") 157 } 158 ctx.Infof("Your password has been updated.") 159 return nil 160 } 161 162 func (c *ChangePasswordCommand) writeEnvironmentFile(ctx *cmd.Context) error { 163 outPath := c.OutPath 164 if outPath == "" { 165 outPath = c.User + ".jenv" 166 } 167 outPath = normaliseJenvPath(ctx, outPath) 168 if err := generateUserJenv(c.ConnectionName(), c.User, c.Password, outPath); err != nil { 169 return err 170 } 171 fmt.Fprintf(ctx.Stdout, "environment file written to %s\n", outPath) 172 return nil 173 }