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  }