func (st *State) loginV1(tag, password, nonce string) error { var result struct { // TODO (cmars): remove once we can drop 1.18 login compatibility params.LoginResult params.LoginResultV1 } err := st.APICall("Admin", 1, "", "Login", ¶ms.LoginRequestCompat{ LoginRequest: params.LoginRequest{ AuthTag: tag, Credentials: password, Nonce: nonce, }, // TODO (cmars): remove once we can drop 1.18 login compatibility Creds: params.Creds{ AuthTag: tag, Password: password, Nonce: nonce, }, }, &result) if err != nil { return err } // We've either logged into an Admin v1 facade, or a pre-facade (1.18) API // server. The JSON field names between the structures are disjoint, so only // one should have an environ tag set. var environTag string var serverTag string var servers [][]network.HostPort var facades []params.FacadeVersions // For quite old servers, it is possible that they don't send down // the environTag. if result.LoginResult.EnvironTag != "" { environTag = result.LoginResult.EnvironTag // If the server doesn't support login v1, it doesn't support // multiple environments, so don't store a server tag. servers = params.NetworkHostsPorts(result.LoginResult.Servers) facades = result.LoginResult.Facades } else if result.LoginResultV1.EnvironTag != "" { environTag = result.LoginResultV1.EnvironTag serverTag = result.LoginResultV1.ServerTag servers = params.NetworkHostsPorts(result.LoginResultV1.Servers) facades = result.LoginResultV1.Facades } err = st.setLoginResult(tag, environTag, serverTag, servers, facades) if err != nil { return err } return nil }
func (st *state) loginForVersion(tag names.Tag, password, nonce string, macaroons []macaroon.Slice, vers int) error { var result params.LoginResultV1 request := ¶ms.LoginRequest{ AuthTag: tagToString(tag), Credentials: password, Nonce: nonce, Macaroons: macaroons, } if tag == nil { // 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", vers, "", "Login", request, &result) if err != nil { return errors.Trace(err) } if result.DischargeRequired != 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" } if err := st.bakeryClient.HandleError(st.cookieURL, &httpbakery.Error{ Message: result.DischargeRequiredReason, Code: httpbakery.ErrDischargeRequired, Info: &httpbakery.ErrorInfo{ Macaroon: result.DischargeRequired, MacaroonPath: "/", }, }); err != nil { 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.LoginResultV1{} // zero result err = st.APICall("Admin", vers, "", "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) } } servers := params.NetworkHostsPorts(result.Servers) err = st.setLoginResult(tag, result.ModelTag, result.ControllerTag, servers, result.Facades) if 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) loginV0(tag, password, nonce string) error { var result params.LoginResult err := st.APICall("Admin", 0, "", "Login", ¶ms.Creds{ AuthTag: tag, Password: password, Nonce: nonce, }, &result) if err != nil { return err } servers := params.NetworkHostsPorts(result.Servers) // Don't set a server tag. if err = st.setLoginResult(tag, result.EnvironTag, "", servers, result.Facades); err != nil { return err } return nil }
func (st *State) loginV2(tag, password, nonce string) error { var result params.LoginResultV1 request := ¶ms.LoginRequest{ AuthTag: tag, Credentials: password, Nonce: nonce, } err := st.APICall("Admin", 2, "", "Login", request, &result) if err != nil { // If the server complains about an empty tag it may be that we are // talking to an older server version that does not understand facades and // expects a params.Creds request instead of a params.LoginRequest. We // return a CodNotImplemented error to force login down to V1, which // supports older server logins. This may mask an actual empty tag in // params.LoginRequest, but that would be picked up in loginV1. V1 will // also produce a warning that we are ignoring an invalid API, so we do not // need to add one here. if err.Error() == `"" is not a valid tag` { return ¶ms.Error{ Message: err.Error(), Code: params.CodeNotImplemented, } } return errors.Trace(err) } servers := params.NetworkHostsPorts(result.Servers) err = st.setLoginResult(tag, result.EnvironTag, result.ServerTag, servers, result.Facades) if 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) loginV2(tag names.Tag, password, nonce string) error { var result params.LoginResultV1 request := ¶ms.LoginRequest{ AuthTag: tagToString(tag), Credentials: password, Nonce: nonce, } if tag == nil { // Add any macaroons that might work for authenticating the login request. request.Macaroons = httpbakery.MacaroonsForURL(st.bakeryClient.Client.Jar, st.cookieURL) } err := st.APICall("Admin", 2, "", "Login", request, &result) if err != nil { // If the server complains about an empty tag it may be that we are // talking to an older server version that does not understand facades and // expects a params.Creds request instead of a params.LoginRequest. We // return a CodNotImplemented error to force login down to V1, which // supports older server logins. This may mask an actual empty tag in // params.LoginRequest, but that would be picked up in loginV1. V1 will // also produce a warning that we are ignoring an invalid API, so we do not // need to add one here. if err.Error() == `"" is not a valid tag` { return ¶ms.Error{ Message: err.Error(), Code: params.CodeNotImplemented, } } return errors.Trace(err) } if result.DischargeRequired != 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" } if err := st.bakeryClient.HandleError(st.cookieURL, &httpbakery.Error{ Message: result.DischargeRequiredReason, Code: httpbakery.ErrDischargeRequired, Info: &httpbakery.ErrorInfo{ Macaroon: result.DischargeRequired, MacaroonPath: "/", }, }); err != nil { 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.LoginResultV1{} // zero result err = st.APICall("Admin", 2, "", "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) } } servers := params.NetworkHostsPorts(result.Servers) err = st.setLoginResult(tag, result.EnvironTag, result.ServerTag, servers, result.Facades) if err != nil { return errors.Trace(err) } st.serverVersion, err = version.Parse(result.ServerVersion) if err != nil { return errors.Trace(err) } return nil }