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