github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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 "encoding/asn1" 8 "encoding/base64" 9 "fmt" 10 "strings" 11 12 "github.com/juju/cmd" 13 "github.com/juju/errors" 14 "github.com/juju/names" 15 "launchpad.net/gnuflag" 16 17 "github.com/juju/juju/cmd/juju/block" 18 "github.com/juju/juju/cmd/modelcmd" 19 "github.com/juju/juju/juju/permission" 20 "github.com/juju/juju/jujuclient" 21 ) 22 23 var usageSummary = ` 24 Adds a Juju user to a controller.`[1:] 25 26 var usageDetails = ` 27 This allows the user to register with the controller and use the 28 optionally shared model. 29 A ` + "`juju register`" + ` command will be printed, which 30 must be executed by the user to complete the registration process. The 31 user's details are stored within the shared model, and will be removed 32 when the model is destroyed. 33 Some machine providers will require the user 34 to be in possession of certain credentials in order to create a model. 35 36 Examples: 37 juju add-user bob 38 juju add-user --share mymodel bob 39 juju add-user --controller mycontroller bob 40 41 See also: 42 register 43 grant 44 show-user 45 list-users 46 switch-user 47 disable-user 48 enable-user 49 change-user-password`[1:] 50 51 // AddUserAPI defines the usermanager API methods that the add command uses. 52 type AddUserAPI interface { 53 AddUser(username, displayName, password, access string, modelUUIDs ...string) (names.UserTag, []byte, error) 54 Close() error 55 } 56 57 func NewAddCommand() cmd.Command { 58 return modelcmd.WrapController(&addCommand{}) 59 } 60 61 // addCommand adds new users into a Juju Server. 62 type addCommand struct { 63 modelcmd.ControllerCommandBase 64 api AddUserAPI 65 User string 66 DisplayName string 67 ModelNames string 68 ModelAccess string 69 } 70 71 func (c *addCommand) SetFlags(f *gnuflag.FlagSet) { 72 f.StringVar(&c.ModelNames, "models", "", "Models the new user is granted access to") 73 f.StringVar(&c.ModelAccess, "acl", "read", "Access controls") 74 } 75 76 // Info implements Command.Info. 77 func (c *addCommand) Info() *cmd.Info { 78 return &cmd.Info{ 79 Name: "add-user", 80 Args: "<user name> [<display name>]", 81 Purpose: usageSummary, 82 Doc: usageDetails, 83 } 84 } 85 86 // Init implements Command.Init. 87 func (c *addCommand) Init(args []string) error { 88 if len(args) == 0 { 89 return fmt.Errorf("no username supplied") 90 } 91 92 _, err := permission.ParseModelAccess(c.ModelAccess) 93 if err != nil { 94 return err 95 } 96 97 c.User, args = args[0], args[1:] 98 if len(args) > 0 { 99 c.DisplayName, args = args[0], args[1:] 100 } 101 return cmd.CheckEmpty(args) 102 } 103 104 // Run implements Command.Run. 105 func (c *addCommand) Run(ctx *cmd.Context) error { 106 api := c.api 107 if api == nil { 108 var err error 109 api, err = c.NewUserManagerAPIClient() 110 if err != nil { 111 return errors.Trace(err) 112 } 113 defer api.Close() 114 } 115 116 var modelNames []string 117 for _, modelArg := range strings.Split(c.ModelNames, ",") { 118 modelArg = strings.TrimSpace(modelArg) 119 if len(modelArg) > 0 { 120 modelNames = append(modelNames, modelArg) 121 } 122 } 123 124 // If we need to share a model, look up the model UUIDs from the supplied names. 125 modelUUIDs, err := c.ModelUUIDs(modelNames) 126 if err != nil { 127 return errors.Trace(err) 128 } 129 130 // Add a user without a password. This will generate a temporary 131 // secret key, which we'll print out for the user to supply to 132 // "juju register". 133 _, secretKey, err := api.AddUser(c.User, c.DisplayName, "", c.ModelAccess, modelUUIDs...) 134 if err != nil { 135 return block.ProcessBlockedError(err, block.BlockChange) 136 } 137 138 displayName := c.User 139 if c.DisplayName != "" { 140 displayName = fmt.Sprintf("%s (%s)", c.DisplayName, c.User) 141 } 142 143 // Generate the base64-encoded string for the user to pass to 144 // "juju register". We marshal the information using ASN.1 145 // to keep the size down, since we need to encode binary data. 146 controllerDetails, err := c.ClientStore().ControllerByName(c.ControllerName()) 147 if err != nil { 148 return errors.Trace(err) 149 } 150 registrationInfo := jujuclient.RegistrationInfo{ 151 User: c.User, 152 Addrs: controllerDetails.APIEndpoints, 153 SecretKey: secretKey, 154 } 155 registrationData, err := asn1.Marshal(registrationInfo) 156 if err != nil { 157 return errors.Trace(err) 158 } 159 160 // Use URLEncoding so we don't get + or / in the string, 161 // and pad with zero bytes so we don't get =; this all 162 // makes it easier to copy & paste in a terminal. 163 // 164 // The embedded ASN.1 data is length-encoded, so the 165 // padding will not complicate decoding. 166 remainder := len(registrationData) % 3 167 for remainder > 0 { 168 registrationData = append(registrationData, 0) 169 remainder-- 170 } 171 base64RegistrationData := base64.URLEncoding.EncodeToString( 172 registrationData, 173 ) 174 175 fmt.Fprintf(ctx.Stdout, "User %q added\n", displayName) 176 for _, modelName := range modelNames { 177 fmt.Fprintf(ctx.Stdout, "User %q granted %s access to model %q\n", displayName, c.ModelAccess, modelName) 178 } 179 fmt.Fprintf(ctx.Stdout, "Please send this command to %v:\n", c.User) 180 fmt.Fprintf(ctx.Stdout, " juju register %s\n", 181 base64RegistrationData, 182 ) 183 if len(modelNames) == 0 { 184 fmt.Fprintf(ctx.Stdout, ` 185 %q has not been granted access to any models. You can use "juju grant" to grant access. 186 `, displayName) 187 } 188 189 return nil 190 }