func (st *state) Login(tag names.Tag, password, nonce string, macaroons []macaroon.Slice) error {
	var result params.LoginResult
	request := &params.LoginRequest{
		AuthTag:       tagToString(tag),
		Credentials:   password,
		Nonce:         nonce,
		Macaroons:     macaroons,
		BakeryVersion: bakery.LatestVersion,
		CLIArgs:       utils.CommandString(os.Args...),
		ClientVersion: jujuversion.Current.String(),
	}
	// If we are in developer mode, add the stack location as user data to the
	// login request. This will allow the apiserver to connect connection ids
	// to the particular place that initiated the connection.
	if featureflag.Enabled(feature.DeveloperMode) {
		request.UserData = string(debug.Stack())
	}

	if password == "" {
		// Add any macaroons from the cookie jar that might work for
		// authenticating the login request.
		request.Macaroons = append(request.Macaroons,
			httpbakery.MacaroonsForURL(st.bakeryClient.Client.Jar, st.cookieURL)...,
		)
	}
	err := st.APICall("Admin", 3, "", "Login", request, &result)
	if err != nil {
		if !params.IsRedirect(err) {
			return errors.Trace(err)
		}

		if rpcErr, ok := errors.Cause(err).(*rpc.RequestError); ok {
			var redirInfo params.RedirectErrorInfo
			err := rpcErr.UnmarshalInfo(&redirInfo)
			if err == nil && redirInfo.CACert != "" && len(redirInfo.Servers) != 0 {
				var controllerTag names.ControllerTag
				if redirInfo.ControllerTag != "" {
					if controllerTag, err = names.ParseControllerTag(redirInfo.ControllerTag); err != nil {
						return errors.Trace(err)
					}
				}

				return &RedirectError{
					Servers:         params.ToMachineHostsPorts(redirInfo.Servers),
					CACert:          redirInfo.CACert,
					ControllerTag:   controllerTag,
					ControllerAlias: redirInfo.ControllerAlias,
					FollowRedirect:  false, // user-action required
				}
			}
		}

		// We've been asked to redirect. Find out the redirection info.
		// If the rpc packet allowed us to return arbitrary information in
		// an error, we'd probably put this information in the Login response,
		// but we can't do that currently.
		var resp params.RedirectInfoResult
		if err := st.APICall("Admin", 3, "", "RedirectInfo", nil, &resp); err != nil {
			return errors.Annotatef(err, "cannot get redirect addresses")
		}
		return &RedirectError{
			Servers:        params.ToMachineHostsPorts(resp.Servers),
			CACert:         resp.CACert,
			FollowRedirect: true, // JAAS-type redirect
		}
	}
	if result.DischargeRequired != nil || result.BakeryDischargeRequired != nil {
		// The result contains a discharge-required
		// macaroon. We discharge it and retry
		// the login request with the original macaroon
		// and its discharges.
		if result.DischargeRequiredReason == "" {
			result.DischargeRequiredReason = "no reason given for discharge requirement"
		}
		// Prefer the newer bakery.v2 macaroon.
		dcMac := result.BakeryDischargeRequired
		if dcMac == nil {
			dcMac, err = bakery.NewLegacyMacaroon(result.DischargeRequired)
			if err != nil {
				return errors.Trace(err)
			}
		}
		if err := st.bakeryClient.HandleError(st.ctx, st.cookieURL, &httpbakery.Error{
			Message: result.DischargeRequiredReason,
			Code:    httpbakery.ErrDischargeRequired,
			Info: &httpbakery.ErrorInfo{
				Macaroon:     dcMac,
				MacaroonPath: "/",
			},
		}); err != nil {
			cause := errors.Cause(err)
			if httpbakery.IsInteractionError(cause) {
				// Just inform the user of the reason for the
				// failure, e.g. because the username/password
				// they presented was invalid.
				err = cause.(*httpbakery.InteractionError).Reason
			}
			return errors.Trace(err)
		}
		// Add the macaroons that have been saved by HandleError to our login request.
		request.Macaroons = httpbakery.MacaroonsForURL(st.bakeryClient.Client.Jar, st.cookieURL)
		result = params.LoginResult{} // zero result
		err = st.APICall("Admin", 3, "", "Login", request, &result)
		if err != nil {
			return errors.Trace(err)
		}
		if result.DischargeRequired != nil {
			return errors.Errorf("login with discharged macaroons failed: %s", result.DischargeRequiredReason)
		}
	}

	var controllerAccess string
	var modelAccess string
	if result.UserInfo != nil {
		tag, err = names.ParseTag(result.UserInfo.Identity)
		if err != nil {
			return errors.Trace(err)
		}
		controllerAccess = result.UserInfo.ControllerAccess
		modelAccess = result.UserInfo.ModelAccess
	}
	servers := params.ToMachineHostsPorts(result.Servers)
	if err = st.setLoginResult(loginResultParams{
		tag:              tag,
		modelTag:         result.ModelTag,
		controllerTag:    result.ControllerTag,
		servers:          servers,
		publicDNSName:    result.PublicDNSName,
		facades:          result.Facades,
		modelAccess:      modelAccess,
		controllerAccess: controllerAccess,
	}); err != nil {
		return errors.Trace(err)
	}
	st.serverVersion, err = version.Parse(result.ServerVersion)
	if err != nil {
		return errors.Trace(err)
	}
	return nil
}
// Login authenticates as the entity with the given name and password
// or macaroons. Subsequent requests on the state will act as that entity.
// This method is usually called automatically by Open. The machine nonce
// should be empty unless logging in as a machine agent.
Login authenticates as the entity with the given name and password
or macaroons. Subsequent requests on the state will act as that entity.
This method is usually called automatically by Open. The machine nonce
should be empty unless logging in as a machine agent.
// Login authenticates as the entity with the given name and password // or macaroons . Subsequent requests on the state will act as that entity . // This method is usually called automatically by Open . The machine nonce // should be empty unless logging in as a machine agent .
func (st *state) Login(tag names.Tag, password, nonce string, macaroons []macaroon.Slice) error {
	var result params.LoginResult
	request := &params.LoginRequest{
		AuthTag:       tagToString(tag),
		Credentials:   password,
		Nonce:         nonce,
		Macaroons:     macaroons,
		BakeryVersion: bakery.LatestVersion,
		CLIArgs:       utils.CommandString(os.Args...),
		ClientVersion: jujuversion.Current.String(),
	}
	if featureflag.Enabled(feature.DeveloperMode) {
		request.UserData = string(debug.Stack())
	}
	if password == "" {
		request.Macaroons = append(request.Macaroons,
			httpbakery.MacaroonsForURL(st.bakeryClient.Client.Jar, st.cookieURL)...,
		)
	}
	err := st.APICall("Admin", 3, "", "Login", request, &result)
	if err != nil {
		if !params.IsRedirect(err) {
			return errors.Trace(err)
		}
		if rpcErr, ok := errors.Cause(err).(*rpc.RequestError); ok {
			var redirInfo params.RedirectErrorInfo
			err := rpcErr.UnmarshalInfo(&redirInfo)
			if err == nil && redirInfo.CACert != "" && len(redirInfo.Servers) != 0 {
				var controllerTag names.ControllerTag
				if redirInfo.ControllerTag != "" {
					if controllerTag, err = names.ParseControllerTag(redirInfo.ControllerTag); err != nil {
						return errors.Trace(err)
					}
				}
				return &RedirectError{
					Servers:         params.ToMachineHostsPorts(redirInfo.Servers),
					CACert:          redirInfo.CACert,
					ControllerTag:   controllerTag,
					ControllerAlias: redirInfo.ControllerAlias,
					FollowRedirect:  false, 
				}
			}
		}
		var resp params.RedirectInfoResult
		if err := st.APICall("Admin", 3, "", "RedirectInfo", nil, &resp); err != nil {
			return errors.Annotatef(err, "cannot get redirect addresses")
		}
		return &RedirectError{
			Servers:        params.ToMachineHostsPorts(resp.Servers),
			CACert:         resp.CACert,
			FollowRedirect: true, 
		}
	}
	if result.DischargeRequired != nil || result.BakeryDischargeRequired != nil {
		if result.DischargeRequiredReason == "" {
			result.DischargeRequiredReason = "no reason given for discharge requirement"
		}
		dcMac := result.BakeryDischargeRequired
		if dcMac == nil {
			dcMac, err = bakery.NewLegacyMacaroon(result.DischargeRequired)
			if err != nil {
				return errors.Trace(err)
			}
		}
		if err := st.bakeryClient.HandleError(st.ctx, st.cookieURL, &httpbakery.Error{
			Message: result.DischargeRequiredReason,
			Code:    httpbakery.ErrDischargeRequired,
			Info: &httpbakery.ErrorInfo{
				Macaroon:     dcMac,
				MacaroonPath: "/",
			},
		}); err != nil {
			cause := errors.Cause(err)
			if httpbakery.IsInteractionError(cause) {
				err = cause.(*httpbakery.InteractionError).Reason
			}
			return errors.Trace(err)
		}
		request.Macaroons = httpbakery.MacaroonsForURL(st.bakeryClient.Client.Jar, st.cookieURL)
		result = params.LoginResult{} 
		err = st.APICall("Admin", 3, "", "Login", request, &result)
		if err != nil {
			return errors.Trace(err)
		}
		if result.DischargeRequired != nil {
			return errors.Errorf("login with discharged macaroons failed: %s", result.DischargeRequiredReason)
		}
	}
	var controllerAccess string
	var modelAccess string
	if result.UserInfo != nil {
		tag, err = names.ParseTag(result.UserInfo.Identity)
		if err != nil {
			return errors.Trace(err)
		}
		controllerAccess = result.UserInfo.ControllerAccess
		modelAccess = result.UserInfo.ModelAccess
	}
	servers := params.ToMachineHostsPorts(result.Servers)
	if err = st.setLoginResult(loginResultParams{
		tag:              tag,
		modelTag:         result.ModelTag,
		controllerTag:    result.ControllerTag,
		servers:          servers,
		publicDNSName:    result.PublicDNSName,
		facades:          result.Facades,
		modelAccess:      modelAccess,
		controllerAccess: controllerAccess,
	}); err != nil {
		return errors.Trace(err)
	}
	st.serverVersion, err = version.Parse(result.ServerVersion)
	if err != nil {
		return errors.Trace(err)
	}
	return nil
}
func ( st * state ) Login ( tag names . Tag , password , nonce string , macaroons [ ] macaroon . Slice ) error { var result params . LoginResult request := & params . LoginRequest { AuthTag : tagToString ( tag ) , Credentials : password , Nonce : nonce , Macaroons : macaroons , BakeryVersion : bakery . LatestVersion , CLIArgs : utils . CommandString ( os . Args ... ) , ClientVersion : jujuversion . Current . String ( ) , } if featureflag . Enabled ( feature . DeveloperMode ) { request . UserData = string ( debug . Stack ( ) ) } if password == " " { request . Macaroons = append ( request . Macaroons , httpbakery . MacaroonsForURL ( st . bakeryClient . Client . Jar , st . cookieURL ) ... , ) } err := st . APICall ( " " , 3 , " " , " " , request , & result ) if err != nil { if ! params . IsRedirect ( err ) { return errors . Trace ( err ) } if rpcErr , ok := errors . Cause ( err ) . ( * rpc . RequestError ) ; ok { var redirInfo params . RedirectErrorInfo err := rpcErr . UnmarshalInfo ( & redirInfo ) if err == nil && redirInfo . CACert != " " && len ( redirInfo . Servers ) != 0 { var controllerTag names . ControllerTag if redirInfo . ControllerTag != " " { if controllerTag , err = names . ParseControllerTag ( redirInfo . ControllerTag ) ; err != nil { return errors . Trace ( err ) } } return & RedirectError { Servers : params . ToMachineHostsPorts ( redirInfo . Servers ) , CACert : redirInfo . CACert , ControllerTag : controllerTag , ControllerAlias : redirInfo . ControllerAlias , FollowRedirect : false , } } } var resp params . RedirectInfoResult if err := st . APICall ( " " , 3 , " " , " " , nil , & resp ) ; err != nil { return errors . Annotatef ( err , " " ) } return & RedirectError { Servers : params . ToMachineHostsPorts ( resp . Servers ) , CACert : resp . CACert , FollowRedirect : true , } } if result . DischargeRequired != nil || result . BakeryDischargeRequired != nil { if result . DischargeRequiredReason == " " { result . DischargeRequiredReason = " " } dcMac := result . BakeryDischargeRequired if dcMac == nil { dcMac , err = bakery . NewLegacyMacaroon ( result . DischargeRequired ) if err != nil { return errors . Trace ( err ) } } if err := st . bakeryClient . HandleError ( st . ctx , st . cookieURL , & httpbakery . Error { Message : result . DischargeRequiredReason , Code : httpbakery . ErrDischargeRequired , Info : & httpbakery . ErrorInfo { Macaroon : dcMac , MacaroonPath : " " , } , } ) ; err != nil { cause := errors . Cause ( err ) if httpbakery . IsInteractionError ( cause ) { err = cause . ( * httpbakery . InteractionError ) . Reason } return errors . Trace ( err ) } request . Macaroons = httpbakery . MacaroonsForURL ( st . bakeryClient . Client . Jar , st . cookieURL ) result = params . LoginResult { } err = st . APICall ( " " , 3 , " " , " " , request , & result ) if err != nil { return errors . Trace ( err ) } if result . DischargeRequired != nil { return errors . Errorf ( " " , result . DischargeRequiredReason ) } } var controllerAccess string var modelAccess string if result . UserInfo != nil { tag , err = names . ParseTag ( result . UserInfo . Identity ) if err != nil { return errors . Trace ( err ) } controllerAccess = result . UserInfo . ControllerAccess modelAccess = result . UserInfo . ModelAccess } servers := params . ToMachineHostsPorts ( result . Servers ) if err = st . setLoginResult ( loginResultParams { tag : tag , modelTag : result . ModelTag , controllerTag : result . ControllerTag , servers : servers , publicDNSName : result . PublicDNSName , facades : result . Facades , modelAccess : modelAccess , controllerAccess : controllerAccess , } ) ; err != nil { return errors . Trace ( err ) } st . serverVersion , err = version . Parse ( result . ServerVersion ) if err != nil { return errors . Trace ( err ) } return nil }
Login authenticates as the entity with the given name and password
or macaroons.
Login authenticates as the entity with the given name and password or macaroons .
// If we are in developer mode, add the stack location as user data to the // login request. This will allow the apiserver to connect connection ids // to the particular place that initiated the connection. // Add any macaroons from the cookie jar that might work for // authenticating the login request. // user-action required // We've been asked to redirect. Find out the redirection info. // If the rpc packet allowed us to return arbitrary information in // an error, we'd probably put this information in the Login response, // but we can't do that currently. // JAAS-type redirect // The result contains a discharge-required // macaroon. We discharge it and retry // the login request with the original macaroon // and its discharges. // Prefer the newer bakery.v2 macaroon. // Just inform the user of the reason for the // failure, e.g. because the username/password // they presented was invalid. // Add the macaroons that have been saved by HandleError to our login request. // zero result