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 (s *ClientSuite) TestMacaroonsForURL(c *gc.C) { // Create a target service. svc := newService("loc", nil) m1, err := svc.NewMacaroon("id1", []byte("key1"), nil) c.Assert(err, gc.IsNil) m2, err := svc.NewMacaroon("id2", []byte("key2"), nil) c.Assert(err, gc.IsNil) u1 := mustParseURL("http://0.1.2.3/") u2 := mustParseURL("http://0.1.2.3/x/") // Create some cookies with different cookie paths. jar, err := cookiejar.New(nil) c.Assert(err, gc.IsNil) httpbakery.SetCookie(jar, u1, macaroon.Slice{m1}) httpbakery.SetCookie(jar, u2, macaroon.Slice{m2}) jar.SetCookies(u1, []*http.Cookie{{ Name: "foo", Path: "/", Value: "ignored", }, { Name: "bar", Path: "/x/", Value: "ignored", }}) // Check that MacaroonsForURL behaves correctly // with both single and multiple cookies. mss := httpbakery.MacaroonsForURL(jar, u1) c.Assert(mss, gc.HasLen, 1) c.Assert(mss[0], gc.HasLen, 1) c.Assert(mss[0][0].Id(), gc.Equals, "id1") mss = httpbakery.MacaroonsForURL(jar, u2) checked := make(map[string]int) for _, ms := range mss { checked[ms[0].Id()]++ err := svc.Check(ms, checkers.New()) c.Assert(err, gc.IsNil) } c.Assert(checked, jc.DeepEquals, map[string]int{ "id1": 1, "id2": 1, }) }
func (c *migrateCommand) getTargetControllerMacaroons() ([]macaroon.Slice, error) { apiContext, err := c.APIContext() if err != nil { return nil, errors.Trace(err) } // Connect to the target controller, ensuring up-to-date macaroons, // and return the macaroons in the cookie jar for the controller. // // TODO(axw,mjs) add a controller API that returns a macaroon that // may be used for the sole purpose of migration. api, err := c.newAPIRoot(c.ClientStore(), c.targetController, "") if err != nil { return nil, errors.Annotate(err, "connecting to target controller") } defer api.Close() return httpbakery.MacaroonsForURL(apiContext.Jar, api.CookieURL()), nil }
func (s *macaroonLoginSuite) login(c *gc.C, info *api.Info) (params.LoginResult, error) { info.SkipLogin = true cookieJar := apitesting.NewClearableCookieJar() client := s.OpenAPI(c, info, cookieJar) defer client.Close() var ( // Remote users start with an empty login request. request params.LoginRequest result params.LoginResult ) err := client.APICall("Admin", 3, "", "Login", &request, &result) c.Assert(err, jc.ErrorIsNil) cookieURL := &url.URL{ Scheme: "https", Host: "localhost", Path: "/", } bakeryClient := httpbakery.NewClient() err = bakeryClient.HandleError(cookieURL, &httpbakery.Error{ Message: result.DischargeRequiredReason, Code: httpbakery.ErrDischargeRequired, Info: &httpbakery.ErrorInfo{ Macaroon: result.DischargeRequired, MacaroonPath: "/", }, }) c.Assert(err, jc.ErrorIsNil) // Add the macaroons that have been saved by HandleError to our login request. request.Macaroons = httpbakery.MacaroonsForURL(bakeryClient.Client.Jar, cookieURL) err = client.APICall("Admin", 3, "", "Login", &request, &result) return result, err }
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 }