github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/user/list.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for infos.
     3  
     4  package user
     5  
     6  import (
     7  	"io"
     8  
     9  	"github.com/juju/ansiterm"
    10  	"github.com/juju/clock"
    11  	"github.com/juju/cmd"
    12  	"github.com/juju/collections/set"
    13  	"github.com/juju/errors"
    14  	"github.com/juju/gnuflag"
    15  	"gopkg.in/juju/names.v2"
    16  
    17  	"github.com/juju/juju/api/usermanager"
    18  	"github.com/juju/juju/apiserver/params"
    19  	jujucmd "github.com/juju/juju/cmd"
    20  	"github.com/juju/juju/cmd/juju/common"
    21  	"github.com/juju/juju/cmd/modelcmd"
    22  	"github.com/juju/juju/cmd/output"
    23  )
    24  
    25  var usageListUsersSummary = `
    26  Lists Juju users allowed to connect to a controller or model.`[1:]
    27  
    28  var usageListUsersDetails = `
    29  When used without a model name argument, users relevant to a controller are printed.
    30  When used with a model name, users relevant to the specified model are printed.
    31  
    32  Examples:
    33      Print the users relevant to the current controller: 
    34      juju users
    35      
    36      Print the users relevant to the controller "another":
    37      juju users -c another
    38  
    39      Print the users relevant to the model "mymodel":
    40      juju users mymodel
    41  
    42  See also: 
    43      add-user
    44      register
    45      show-user
    46      disable-user
    47      enable-user`[1:]
    48  
    49  func NewListCommand() cmd.Command {
    50  	return modelcmd.WrapController(&listCommand{
    51  		infoCommandBase: infoCommandBase{
    52  			clock: clock.WallClock,
    53  		},
    54  	})
    55  }
    56  
    57  // listCommand shows all the users in the Juju server.
    58  type listCommand struct {
    59  	infoCommandBase
    60  	modelUserAPI modelUsersAPI
    61  
    62  	All         bool
    63  	modelName   string
    64  	currentUser string
    65  }
    66  
    67  // ModelUsersAPI defines the methods on the client API that the
    68  // users command calls.
    69  type modelUsersAPI interface {
    70  	Close() error
    71  	ModelUserInfo() ([]params.ModelUserInfo, error)
    72  }
    73  
    74  func (c *listCommand) getModelAPI() (modelUsersAPI, error) {
    75  	if c.modelUserAPI != nil {
    76  		return c.modelUserAPI, nil
    77  	}
    78  	conn, err := c.NewModelAPIRoot(c.modelName)
    79  	if err != nil {
    80  		return nil, errors.Trace(err)
    81  	}
    82  	return conn.Client(), nil
    83  }
    84  
    85  // Info implements Command.Info.
    86  func (c *listCommand) Info() *cmd.Info {
    87  	return jujucmd.Info(&cmd.Info{
    88  		Name:    "users",
    89  		Purpose: usageListUsersSummary,
    90  		Doc:     usageListUsersDetails,
    91  		Aliases: []string{"list-users"},
    92  	})
    93  }
    94  
    95  // SetFlags implements Command.SetFlags.
    96  func (c *listCommand) SetFlags(f *gnuflag.FlagSet) {
    97  	c.infoCommandBase.SetFlags(f)
    98  	f.BoolVar(&c.All, "all", false, "Include disabled users")
    99  	c.out.AddFlags(f, "tabular", map[string]cmd.Formatter{
   100  		"yaml":    cmd.FormatYaml,
   101  		"json":    cmd.FormatJson,
   102  		"tabular": c.formatTabular,
   103  	})
   104  }
   105  
   106  // Init implements Command.Init.
   107  func (c *listCommand) Init(args []string) (err error) {
   108  	c.modelName, err = cmd.ZeroOrOneArgs(args)
   109  	if err != nil {
   110  		return err
   111  	}
   112  	return err
   113  }
   114  
   115  // Run implements Command.Run.
   116  func (c *listCommand) Run(ctx *cmd.Context) (err error) {
   117  	if c.out.Name() == "tabular" {
   118  		// Only the tabular outputters need to know the current user,
   119  		// but both of them do, so do it in one place.
   120  		accountDetails, err := c.CurrentAccountDetails()
   121  		if err != nil {
   122  			return err
   123  		}
   124  		c.currentUser = names.NewUserTag(accountDetails.User).Id()
   125  	}
   126  	if c.modelName == "" {
   127  		return c.controllerUsers(ctx)
   128  	}
   129  	return c.modelUsers(ctx)
   130  }
   131  
   132  func (c *listCommand) modelUsers(ctx *cmd.Context) error {
   133  	client, err := c.getModelAPI()
   134  	if err != nil {
   135  		return err
   136  	}
   137  	defer client.Close()
   138  
   139  	result, err := client.ModelUserInfo()
   140  	if err != nil {
   141  		return err
   142  	}
   143  	if len(result) == 0 {
   144  		ctx.Infof("No users to display.")
   145  		return nil
   146  	}
   147  	return c.out.Write(ctx, common.ModelUserInfoFromParams(result, c.clock.Now()))
   148  }
   149  
   150  func (c *listCommand) controllerUsers(ctx *cmd.Context) error {
   151  	// Note: the InfoCommandBase and the UserInfo struct are defined
   152  	// in info.go.
   153  	client, err := c.getUserInfoAPI()
   154  	if err != nil {
   155  		return err
   156  	}
   157  	defer client.Close()
   158  
   159  	result, err := client.UserInfo(nil, usermanager.IncludeDisabled(c.All))
   160  	if err != nil {
   161  		return err
   162  	}
   163  
   164  	if len(result) == 0 {
   165  		ctx.Infof("No users to display.")
   166  		return nil
   167  	}
   168  
   169  	return c.out.Write(ctx, c.apiUsersToUserInfoSlice(result))
   170  }
   171  
   172  func (c *listCommand) formatTabular(writer io.Writer, value interface{}) error {
   173  	if c.modelName == "" {
   174  		return c.formatControllerUsers(writer, value)
   175  	}
   176  	return c.formatModelUsers(writer, value)
   177  }
   178  
   179  func (c *listCommand) isLoggedInUser(username string) bool {
   180  	tag := names.NewUserTag(username)
   181  	return tag.Id() == c.currentUser
   182  }
   183  
   184  func (c *listCommand) formatModelUsers(writer io.Writer, value interface{}) error {
   185  	users, ok := value.(map[string]common.ModelUserInfo)
   186  	if !ok {
   187  		return errors.Errorf("expected value of type %T, got %T", users, value)
   188  	}
   189  	modelUsers := set.NewStrings()
   190  	for name := range users {
   191  		modelUsers.Add(name)
   192  	}
   193  	tw := output.TabWriter(writer)
   194  	w := output.Wrapper{tw}
   195  	w.Println("Name", "Display name", "Access", "Last connection")
   196  	for _, name := range modelUsers.SortedValues() {
   197  		user := users[name]
   198  
   199  		var highlight *ansiterm.Context
   200  		userName := name
   201  		if c.isLoggedInUser(name) {
   202  			userName += "*"
   203  			highlight = output.CurrentHighlight
   204  		}
   205  		w.PrintColor(highlight, userName)
   206  		w.Println(user.DisplayName, user.Access, user.LastConnection)
   207  	}
   208  	tw.Flush()
   209  	return nil
   210  }
   211  
   212  func (c *listCommand) formatControllerUsers(writer io.Writer, value interface{}) error {
   213  	controllerName, err := c.ControllerName()
   214  	if err != nil {
   215  		return errors.Trace(err)
   216  	}
   217  	users, valueConverted := value.([]UserInfo)
   218  	if !valueConverted {
   219  		return errors.Errorf("expected value of type %T, got %T", users, value)
   220  	}
   221  
   222  	tw := output.TabWriter(writer)
   223  	w := output.Wrapper{tw}
   224  	w.Println("Controller: " + controllerName)
   225  	w.Println()
   226  	w.Println("Name", "Display name", "Access", "Date created", "Last connection")
   227  	for _, user := range users {
   228  		conn := user.LastConnection
   229  		if user.Disabled {
   230  			conn += " (disabled)"
   231  		}
   232  		var highlight *ansiterm.Context
   233  		userName := user.Username
   234  		if c.isLoggedInUser(user.Username) {
   235  			userName += "*"
   236  			highlight = output.CurrentHighlight
   237  		}
   238  		w.PrintColor(highlight, userName)
   239  		w.Println(user.DisplayName, user.Access, user.DateCreated, conn)
   240  	}
   241  	tw.Flush()
   242  	return nil
   243  }