github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/cmd/juju/user/add.go (about)

     1  // Copyright 2012-2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package user
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"strings"
    10  
    11  	"github.com/juju/cmd"
    12  	"github.com/juju/errors"
    13  	"github.com/juju/names"
    14  	"launchpad.net/gnuflag"
    15  
    16  	"github.com/juju/juju/cmd/juju/block"
    17  	"github.com/juju/juju/environs/configstore"
    18  )
    19  
    20  const userAddCommandDoc = `
    21  Add users to an existing environment.
    22  
    23  The user information is stored within an existing environment, and will be
    24  lost when the environent is destroyed.  An environment file (.jenv) will be
    25  written out in the current directory.  You can control the name and location
    26  of this file using the --output option.
    27  
    28  Examples:
    29    # Add user "foobar". You will be prompted to enter a password.
    30    juju user add foobar
    31  
    32    # Add user "foobar" with a strong random password is generated.
    33    juju user add foobar --generate
    34  
    35  
    36  See Also:
    37    juju user change-password
    38  `
    39  
    40  // AddCommand adds new users into a Juju Server.
    41  type AddCommand struct {
    42  	UserCommandBase
    43  	User        string
    44  	DisplayName string
    45  	Password    string
    46  	OutPath     string
    47  	Generate    bool
    48  }
    49  
    50  // Info implements Command.Info.
    51  func (c *AddCommand) Info() *cmd.Info {
    52  	return &cmd.Info{
    53  		Name:    "add",
    54  		Args:    "<username> [<display name>]",
    55  		Purpose: "adds a user",
    56  		Doc:     userAddCommandDoc,
    57  	}
    58  }
    59  
    60  // SetFlags implements Command.SetFlags.
    61  func (c *AddCommand) SetFlags(f *gnuflag.FlagSet) {
    62  	f.BoolVar(&c.Generate, "generate", false, "generate a new strong password")
    63  	f.StringVar(&c.OutPath, "o", "", "specify the environment file for new user")
    64  	f.StringVar(&c.OutPath, "output", "", "")
    65  }
    66  
    67  // Init implements Command.Init.
    68  func (c *AddCommand) Init(args []string) error {
    69  	if len(args) == 0 {
    70  		return fmt.Errorf("no username supplied")
    71  	}
    72  	c.User, args = args[0], args[1:]
    73  	if len(args) > 0 {
    74  		c.DisplayName, args = args[0], args[1:]
    75  	}
    76  	return cmd.CheckEmpty(args)
    77  }
    78  
    79  // AddUserAPI defines the usermanager API methods that the add command uses.
    80  type AddUserAPI interface {
    81  	AddUser(username, displayName, password string) (names.UserTag, error)
    82  	Close() error
    83  }
    84  
    85  // ShareEnvironmentAPI defines the client API methods that the add command uses.
    86  type ShareEnvironmentAPI interface {
    87  	ShareEnvironment(users []names.UserTag) error
    88  	Close() error
    89  }
    90  
    91  func (c *AddCommand) getAddUserAPI() (AddUserAPI, error) {
    92  	return c.NewUserManagerClient()
    93  }
    94  
    95  func (c *AddCommand) getShareEnvAPI() (ShareEnvironmentAPI, error) {
    96  	return c.NewAPIClient()
    97  }
    98  
    99  var (
   100  	getAddUserAPI  = (*AddCommand).getAddUserAPI
   101  	getShareEnvAPI = (*AddCommand).getShareEnvAPI
   102  )
   103  
   104  // Run implements Command.Run.
   105  func (c *AddCommand) Run(ctx *cmd.Context) error {
   106  	client, err := getAddUserAPI(c)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	defer client.Close()
   111  
   112  	if !c.Generate {
   113  		ctx.Infof("To generate a random strong password, use the --generate flag.")
   114  	}
   115  
   116  	shareClient, err := getShareEnvAPI(c)
   117  	if err != nil {
   118  		return err
   119  	}
   120  	defer shareClient.Close()
   121  
   122  	c.Password, err = c.generateOrReadPassword(ctx, c.Generate)
   123  	if err != nil {
   124  		return errors.Trace(err)
   125  	}
   126  
   127  	tag, err := client.AddUser(c.User, c.DisplayName, c.Password)
   128  	if err != nil {
   129  		return block.ProcessBlockedError(err, block.BlockChange)
   130  	}
   131  	// Until we have multiple environments stored in a state server
   132  	// it makes no sense at all to create a user and not have that user
   133  	// able to log in and use the one and only environment.
   134  	// So we share the existing environment with the user here and now.
   135  	err = shareClient.ShareEnvironment([]names.UserTag{tag})
   136  	if err != nil {
   137  		return err
   138  	}
   139  
   140  	user := c.User
   141  	if c.DisplayName != "" {
   142  		user = fmt.Sprintf("%s (%s)", c.DisplayName, user)
   143  	}
   144  
   145  	fmt.Fprintf(ctx.Stdout, "user %q added\n", user)
   146  	if c.OutPath == "" {
   147  		c.OutPath = c.User + ".jenv"
   148  	}
   149  
   150  	outPath := normaliseJenvPath(ctx, c.OutPath)
   151  	err = generateUserJenv(c.ConnectionName(), c.User, c.Password, outPath)
   152  	if err == nil {
   153  		fmt.Fprintf(ctx.Stdout, "environment file written to %s\n", outPath)
   154  	}
   155  
   156  	return err
   157  }
   158  
   159  func normaliseJenvPath(ctx *cmd.Context, outPath string) string {
   160  	if !strings.HasSuffix(outPath, ".jenv") {
   161  		outPath = outPath + ".jenv"
   162  	}
   163  	return ctx.AbsPath(outPath)
   164  }
   165  
   166  func generateUserJenv(envName, user, password, outPath string) error {
   167  	store, err := configstore.Default()
   168  	if err != nil {
   169  		return errors.Trace(err)
   170  	}
   171  	storeInfo, err := store.ReadInfo(envName)
   172  	if err != nil {
   173  		return errors.Trace(err)
   174  	}
   175  	endpoint := storeInfo.APIEndpoint()
   176  	outputInfo := configstore.EnvironInfoData{
   177  		User:         user,
   178  		Password:     password,
   179  		EnvironUUID:  endpoint.EnvironUUID,
   180  		StateServers: endpoint.Addresses,
   181  		CACert:       endpoint.CACert,
   182  	}
   183  	yaml, err := cmd.FormatYaml(outputInfo)
   184  	if err != nil {
   185  		return errors.Trace(err)
   186  	}
   187  
   188  	outFile, err := os.Create(outPath)
   189  	if err != nil {
   190  		return errors.Trace(err)
   191  	}
   192  	defer outFile.Close()
   193  	outFile.Write(yaml)
   194  	if err != nil {
   195  		return errors.Trace(err)
   196  	}
   197  	return nil
   198  }