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  }