1
// Copyright 2016 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
8
"github.com/juju/errors"
9
"github.com/juju/names"
10
"gopkg.in/macaroon.v1"
12
"github.com/juju/juju/api/usermanager"
13
"github.com/juju/juju/cmd/modelcmd"
14
"github.com/juju/juju/juju"
15
"github.com/juju/juju/jujuclient"
19
Log in to the controller.
21
After logging in successfully, the client system will
22
be updated such that any previously recorded password
23
will be removed from disk, and a temporary, time-limited
24
credential written in its place. Once the credential
25
expires, you will be prompted to run "juju login" again.
28
# Log in as the current user for the controller.
31
# Log in as the user "bob".
36
// NewLoginCommand returns a new cmd.Command to handle "juju login".
37
func NewLoginCommand() cmd.Command {
38
return modelcmd.WrapController(&loginCommand{
39
newLoginAPI: func(args juju.NewAPIConnectionParams) (LoginAPI, error) {
40
api, err := juju.NewAPIConnection(args)
42
return nil, errors.Trace(err)
44
return usermanager.NewClient(api), nil
49
// loginCommand changes the password for a user.
50
type loginCommand struct {
51
modelcmd.ControllerCommandBase
52
newLoginAPI func(juju.NewAPIConnectionParams) (LoginAPI, error)
56
// Info implements Command.Info.
57
func (c *loginCommand) Info() *cmd.Info {
61
Purpose: "logs in to the controller",
66
// Init implements Command.Init.
67
func (c *loginCommand) Init(args []string) error {
69
c.User, err = cmd.ZeroOrOneArgs(args)
71
return errors.Trace(err)
76
// LoginAPI provides the API methods that the login command uses.
77
type LoginAPI interface {
78
CreateLocalLoginMacaroon(names.UserTag) (*macaroon.Macaroon, error)
82
// Run implements Command.Run.
83
func (c *loginCommand) Run(ctx *cmd.Context) error {
84
var accountName string
85
var userTag names.UserTag
86
controllerName := c.ControllerName()
87
store := c.ClientStore()
89
if !names.IsValidUserName(c.User) {
90
return errors.NotValidf("user name %q", c.User)
92
userTag = names.NewUserTag(c.User)
93
accountName = userTag.Canonical()
96
accountName, err = store.CurrentAccount(controllerName)
98
return errors.Trace(err)
100
userTag = names.NewUserTag(accountName)
102
accountDetails, err := store.AccountByName(controllerName, accountName)
103
if err != nil && !errors.IsNotFound(err) {
104
return errors.Trace(err)
107
// Read password from the terminal, and attempt to log in using that.
108
password, err := readAndConfirmPassword(ctx)
110
return errors.Trace(err)
112
params, err := c.NewAPIConnectionParams(store, controllerName, "", "")
114
return errors.Trace(err)
116
if accountDetails != nil {
117
accountDetails.Password = password
119
accountDetails = &jujuclient.AccountDetails{
124
params.AccountDetails = accountDetails
125
api, err := c.newLoginAPI(params)
127
return errors.Annotate(err, "creating API connection")
131
// Create a new local login macaroon, and update the account details
132
// in the client store, removing the recorded password (if any) and
133
// storing the macaroon.
134
macaroon, err := api.CreateLocalLoginMacaroon(userTag)
136
return errors.Annotate(err, "failed to create a temporary credential")
138
macaroonJSON, err := macaroon.MarshalJSON()
140
return errors.Annotate(err, "marshalling temporary credential to JSON")
142
accountDetails.Password = ""
143
accountDetails.Macaroon = string(macaroonJSON)
144
if err := store.UpdateAccount(controllerName, accountName, *accountDetails); err != nil {
145
return errors.Annotate(err, "failed to record temporary credential")
147
ctx.Infof("You are now logged in to %q as %q.", controllerName, accountName)