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

     1  // Copyright 2012-2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  package user
     4  
     5  import (
     6  	"bufio"
     7  	"fmt"
     8  	"io"
     9  	"strings"
    10  
    11  	"github.com/juju/cmd"
    12  	"github.com/juju/errors"
    13  	"github.com/juju/gnuflag"
    14  
    15  	"github.com/juju/juju/apiserver/params"
    16  	jujucmd "github.com/juju/juju/cmd"
    17  	"github.com/juju/juju/cmd/juju/block"
    18  	"github.com/juju/juju/cmd/modelcmd"
    19  )
    20  
    21  var removeUsageSummary = `
    22  Deletes a Juju user from a controller.`[1:]
    23  
    24  // TODO(redir): Get updated copy for add-user as that may need updates, too.
    25  var removeUsageDetails = `
    26  This removes a user permanently.
    27  
    28  By default, the controller is the current controller.
    29  
    30  Examples:
    31      juju remove-user bob
    32      juju remove-user bob --yes
    33  
    34  See also: 
    35      unregister
    36      revoke
    37      show-user
    38      list-users
    39      switch-user
    40      disable-user
    41      enable-user
    42      change-user-password`[1:]
    43  
    44  var removeUserMsg = `
    45  WARNING! This command will permanently archive the user %q on the %q
    46  controller.
    47  
    48  This action is irreversible. If you wish to temporarily disable the
    49  user please use the`[1:] + " `juju disable-user` " + `command. See
    50  ` + " `juju help disable-user` " + `for more details.
    51  
    52  Continue (y/N)? `
    53  
    54  // RemoveUserAPI defines the usermanager API methods that the remove command
    55  // uses.
    56  type RemoveUserAPI interface {
    57  	RemoveUser(username string) error
    58  	Close() error
    59  }
    60  
    61  // NewRemoveCommand constructs a wrapped unexported removeCommand.
    62  func NewRemoveCommand() cmd.Command {
    63  	return modelcmd.WrapController(&removeCommand{})
    64  }
    65  
    66  // removeCommand deletes a user from a Juju controller.
    67  type removeCommand struct {
    68  	modelcmd.ControllerCommandBase
    69  	api           RemoveUserAPI
    70  	UserName      string
    71  	ConfirmDelete bool
    72  }
    73  
    74  // SetFlags adds command specific flags and then returns the flagset.
    75  func (c *removeCommand) SetFlags(f *gnuflag.FlagSet) {
    76  	c.ControllerCommandBase.SetFlags(f)
    77  	f.BoolVar(&c.ConfirmDelete, "y", false, "Confirm deletion of the user")
    78  	f.BoolVar(&c.ConfirmDelete, "yes", false, "")
    79  }
    80  
    81  // Info implements Command.Info.
    82  func (c *removeCommand) Info() *cmd.Info {
    83  	return jujucmd.Info(&cmd.Info{
    84  		Name:    "remove-user",
    85  		Args:    "<user name>",
    86  		Purpose: removeUsageSummary,
    87  		Doc:     removeUsageDetails,
    88  	})
    89  }
    90  
    91  // Init implements Command.Init.
    92  func (c *removeCommand) Init(args []string) error {
    93  	if len(args) == 0 {
    94  		return errors.Errorf("no username supplied")
    95  	}
    96  	c.UserName = args[0]
    97  	return cmd.CheckEmpty(args[1:])
    98  }
    99  
   100  // Run implements Command.Run.
   101  func (c *removeCommand) Run(ctx *cmd.Context) error {
   102  	controllerName, err := c.ControllerName()
   103  	if err != nil {
   104  		return errors.Trace(err)
   105  	}
   106  	api := c.api // This is for testing.
   107  
   108  	if api == nil { // The real McCoy.
   109  		var err error
   110  		api, err = c.NewUserManagerAPIClient()
   111  		if err != nil {
   112  			return errors.Trace(err)
   113  		}
   114  		defer api.Close()
   115  	}
   116  
   117  	// Confirm deletion if the user didn't specify -y/--yes in the command.
   118  	if !c.ConfirmDelete {
   119  		if err := confirmDelete(ctx, controllerName, c.UserName); err != nil {
   120  			return errors.Trace(err)
   121  		}
   122  	}
   123  
   124  	if err := api.RemoveUser(c.UserName); err != nil {
   125  		// This is very awful, but it makes the user experience crisper. At
   126  		// least maybe more tenable until users and authn/z are overhauled.
   127  		if e, ok := err.(*params.Error); ok {
   128  			if e.Message == fmt.Sprintf("failed to delete user %q: user %q is permanently deleted", c.UserName, c.UserName) {
   129  				e.Message = fmt.Sprintf("failed to delete user %q: the user has already been permanently deleted", c.UserName)
   130  				err = e
   131  			}
   132  		}
   133  		return block.ProcessBlockedError(err, block.BlockChange)
   134  	}
   135  
   136  	fmt.Fprintf(ctx.Stdout, "User %q removed\n", c.UserName)
   137  
   138  	return nil
   139  }
   140  
   141  func confirmDelete(ctx *cmd.Context, controller, username string) error {
   142  	// Get confirmation from the user that they want to continue
   143  	fmt.Fprintf(ctx.Stdout, removeUserMsg, username, controller)
   144  
   145  	scanner := bufio.NewScanner(ctx.Stdin)
   146  	scanner.Scan()
   147  	err := scanner.Err()
   148  	if err != nil && err != io.EOF {
   149  		return errors.Annotate(err, "user deletion aborted")
   150  	}
   151  	answer := strings.ToLower(scanner.Text())
   152  	if answer != "y" && answer != "yes" {
   153  		return errors.New("user deletion aborted")
   154  	}
   155  	return nil
   156  }