func (s *baseLoginSuite) openAPIWithoutLogin(c *gc.C, info *api.Info) api.Connection { info.Tag = nil info.Password = "" st, err := api.Open(info, fastDialOpts) c.Assert(err, jc.ErrorIsNil) return st }
func commonConnect( apiOpen api.OpenFunc, apiInfo *api.Info, accountDetails *jujuclient.AccountDetails, modelUUID string, dialOpts api.DialOpts, ) (api.Connection, error) { if accountDetails != nil { // We only set the tag if either a password or // macaroon is found in the accounts.yaml file. // If neither is found, we'll use external // macaroon authentication which requires that // no tag be specified. userTag := names.NewUserTag(accountDetails.User) if accountDetails.Password != "" { // If a password is available, we always use // that. // // TODO(axw) make it invalid to store both // password and macaroon in accounts.yaml? apiInfo.Tag = userTag apiInfo.Password = accountDetails.Password } else if accountDetails.Macaroon != "" { var m macaroon.Macaroon if err := json.Unmarshal([]byte(accountDetails.Macaroon), &m); err != nil { return nil, errors.Trace(err) } apiInfo.Tag = userTag apiInfo.Macaroons = []macaroon.Slice{{&m}} } } if modelUUID != "" { apiInfo.ModelTag = names.NewModelTag(modelUUID) } st, err := apiOpen(apiInfo, dialOpts) return st, errors.Trace(err) }
// Run implements Command.Run func (c *loginCommand) Run(ctx *cmd.Context) error { if c.loginAPIOpen == nil { c.loginAPIOpen = c.JujuCommandBase.APIOpen } // TODO(thumper): as we support the user and address // change this check here. if c.Server.Path == "" { return errors.New("no server file specified") } serverYAML, err := c.Server.Read(ctx) if err != nil { return errors.Trace(err) } var serverDetails envcmd.ServerFile if err := goyaml.Unmarshal(serverYAML, &serverDetails); err != nil { return errors.Trace(err) } info := api.Info{ Addrs: serverDetails.Addresses, CACert: serverDetails.CACert, } var userTag names.UserTag if serverDetails.Username != "" { // Construct the api.Info struct from the provided values // and attempt to connect to the remote server before we do anything else. if !names.IsValidUser(serverDetails.Username) { return errors.Errorf("%q is not a valid username", serverDetails.Username) } userTag = names.NewUserTag(serverDetails.Username) if !userTag.IsLocal() { // Remote users do not have their passwords stored in Juju // so we never attempt to change them. c.KeepPassword = true } info.Tag = userTag } if serverDetails.Password != "" { info.Password = serverDetails.Password } if serverDetails.Password == "" || serverDetails.Username == "" { info.UseMacaroons = true } if c == nil { panic("nil c") } if c.loginAPIOpen == nil { panic("no loginAPIOpen") } apiState, err := c.loginAPIOpen(&info, api.DefaultDialOpts()) if err != nil { return errors.Trace(err) } defer apiState.Close() // If we get to here, the credentials supplied were sufficient to connect // to the Juju Controller and login. Now we cache the details. controllerInfo, err := c.cacheConnectionInfo(serverDetails, apiState) if err != nil { return errors.Trace(err) } ctx.Infof("cached connection details as controller %q", c.Name) // If we get to here, we have been able to connect to the API server, and // also have been able to write the cached information. Now we can change // the user's password to a new randomly generated strong password, and // update the cached information knowing that the likelihood of failure is // minimal. if !c.KeepPassword { if err := c.updatePassword(ctx, apiState, userTag, controllerInfo); err != nil { return errors.Trace(err) } } return errors.Trace(envcmd.SetCurrentController(ctx, c.Name)) }