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 }