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

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package user
     5  
     6  import (
     7  	"github.com/juju/cmd"
     8  	"github.com/juju/errors"
     9  	"github.com/juju/gnuflag"
    10  
    11  	jujucmd "github.com/juju/juju/cmd"
    12  	"github.com/juju/juju/cmd/modelcmd"
    13  	"github.com/juju/juju/jujuclient"
    14  )
    15  
    16  const logoutDoc = `
    17  If another client has logged in as the same user, they will remain logged
    18  in. This command only affects the local client.
    19  
    20  The command will fail if the user has not yet set a password
    21  (` + "`juju change-user-password`" + `). This scenario is only possible after 
    22  ` + "`juju bootstrap`" + `since ` + "`juju register`" + ` sets a password. The
    23  failing behaviour can be overridden with the '--force' option.
    24  
    25  If the same user is logged in with another client system, that user session
    26  will not be affected by this command; it only affects the local client.
    27  
    28  By default, the controller is the current controller.
    29  
    30  Examples:
    31      juju logout
    32  
    33  See also:
    34      change-user-password
    35      login
    36  
    37  `
    38  
    39  // NewLogoutCommand returns a new cmd.Command to handle "juju logout".
    40  func NewLogoutCommand() cmd.Command {
    41  	return modelcmd.WrapController(&logoutCommand{})
    42  }
    43  
    44  // logoutCommand changes the password for a user.
    45  type logoutCommand struct {
    46  	modelcmd.ControllerCommandBase
    47  	Force bool
    48  }
    49  
    50  // Info implements Command.Info.
    51  func (c *logoutCommand) Info() *cmd.Info {
    52  	return jujucmd.Info(&cmd.Info{
    53  		Name:    "logout",
    54  		Purpose: "Logs a Juju user out of a controller.",
    55  		Doc:     logoutDoc,
    56  	})
    57  }
    58  
    59  // Init implements Command.Init.
    60  func (c *logoutCommand) Init(args []string) error {
    61  	return cmd.CheckEmpty(args)
    62  }
    63  
    64  // SetFlags implements Command.SetFlags.
    65  func (c *logoutCommand) SetFlags(f *gnuflag.FlagSet) {
    66  	c.ControllerCommandBase.SetFlags(f)
    67  	f.BoolVar(&c.Force, "force", false, "Force logout when a locally recorded password is detected")
    68  }
    69  
    70  // Run implements Command.Run.
    71  func (c *logoutCommand) Run(ctx *cmd.Context) error {
    72  	controllerName, err := c.ControllerName()
    73  	if err != nil {
    74  		return errors.Trace(err)
    75  	}
    76  	store := c.ClientStore()
    77  	if err := c.logout(store, controllerName); err != nil {
    78  		return errors.Trace(err)
    79  	}
    80  
    81  	// Count the number of logged-into controllers to inform the user.
    82  	var loggedInCount int
    83  	controllers, err := store.AllControllers()
    84  	if err != nil {
    85  		return errors.Trace(err)
    86  	}
    87  	for name := range controllers {
    88  		if name == controllerName {
    89  			continue
    90  		}
    91  		_, err := store.AccountDetails(name)
    92  		if errors.IsNotFound(err) {
    93  			continue
    94  		} else if err != nil {
    95  			return errors.Trace(err)
    96  		}
    97  		loggedInCount++
    98  	}
    99  	switch loggedInCount {
   100  	case 0:
   101  		ctx.Infof("Logged out. You are no longer logged into any controllers.")
   102  	case 1:
   103  		ctx.Infof("Logged out. You are still logged into 1 controller.")
   104  	default:
   105  		ctx.Infof("Logged out. You are still logged into %d controllers.", loggedInCount)
   106  	}
   107  	return nil
   108  }
   109  
   110  func (c *logoutCommand) logout(store jujuclient.ClientStore, controllerName string) error {
   111  	accountDetails, err := store.AccountDetails(controllerName)
   112  	if errors.IsNotFound(err) {
   113  		// Not logged in; nothing else to do.
   114  		return nil
   115  	} else if err != nil {
   116  		return errors.Trace(err)
   117  	}
   118  
   119  	// We first ensure that the user has a macaroon, which implies
   120  	// they know their password. If they have just bootstrapped,
   121  	// they will have a randomly generated password which they will
   122  	// be unaware of.
   123  	if accountDetails.Password != "" && !c.Force {
   124  		return errors.New(`preventing account loss
   125  
   126  It appears that you have not changed the password for
   127  your account. If this is the case, change the password
   128  first before logging out, so that you can log in again
   129  afterwards. To change your password, run the command
   130  "juju change-user-password".
   131  
   132  If you are sure you want to log out, and it is safe to
   133  clear the credentials from the client, then you can run
   134  this command again with the "--force" option.
   135  `)
   136  	}
   137  
   138  	if err := c.ClearControllerMacaroons(c.ClientStore(), controllerName); err != nil {
   139  		return errors.Trace(err)
   140  	}
   141  
   142  	// Remove the account credentials.
   143  	if err := store.RemoveAccount(controllerName); err != nil {
   144  		return errors.Annotate(err, "failed to clear credentials")
   145  	}
   146  	return nil
   147  }