func (d *formDischarger) login(w http.ResponseWriter, r *http.Request) { r.ParseForm() if r.Form.Get("fallback") != "" { d.discharger.FinishInteraction(w, r, nil, nil) return } if d.ignoreAccept { w.Write([]byte("OK")) return } if r.Header.Get("Accept") != "application/json" { d.errorf(w, r, "bad accept header %q", r.Header.Get("Accept")) } if d.loginError { httprequest.WriteJSON(w, http.StatusInternalServerError, testError) d.discharger.FinishInteraction(w, r, nil, testError) return } methods := map[string]string{} if !d.formUnsupported { r.ParseForm() methods["form"] = d.discharger.URL("/form", r) } httprequest.WriteJSON(w, http.StatusOK, methods) }
func (d *formDischarger) visit(w http.ResponseWriter, r *http.Request) { r.ParseForm() if r.Form.Get("fallback") != "" { d.discharger.FinishInteraction(w, r, nil, nil) return } if d.ignoreAccept { w.Write([]byte("OK")) return } if r.Header.Get("Accept") != "application/json" { d.errorf(w, r, "bad accept header %q", r.Header.Get("Accept")) } if d.visitError { httprequest.WriteJSON(w, http.StatusInternalServerError, testError) d.discharger.FinishInteraction(w, r, nil, testError) return } methods := map[string]string{ form.InteractionMethod: d.discharger.HostRelativeURL("/form", r), "othermethod": d.discharger.HostRelativeURL("/visit", r), } if d.formUnsupported { delete(methods, form.InteractionMethod) } httprequest.WriteJSON(w, http.StatusOK, methods) }
func (d *InteractiveDischarger) wait(w http.ResponseWriter, r *http.Request) { r.ParseForm() d.mu.Lock() discharge, ok := d.waiting[r.Form.Get("waitid")] d.mu.Unlock() if !ok { code, body := httpbakery.ErrorToResponse(errgo.Newf("invalid waitid %q", r.Form.Get("waitid"))) httprequest.WriteJSON(w, code, body) return } defer func() { d.mu.Lock() delete(d.waiting, r.Form.Get("waitid")) d.mu.Unlock() }() var err error var cavs []checkers.Caveat select { case res := <-discharge.c: err = res.err cavs = res.cavs case <-time.After(5 * time.Minute): code, body := httpbakery.ErrorToResponse(errgo.New("timeout waiting for interaction to complete")) httprequest.WriteJSON(w, code, body) return } if err != nil { code, body := httpbakery.ErrorToResponse(err) httprequest.WriteJSON(w, code, body) return } m, err := d.Service.Discharge( bakery.ThirdPartyCheckerFunc( func(cavId, caveat string) ([]checkers.Caveat, error) { return cavs, nil }, ), discharge.cavId, ) if err != nil { code, body := httpbakery.ErrorToResponse(err) httprequest.WriteJSON(w, code, body) return } httprequest.WriteJSON( w, http.StatusOK, httpbakery.WaitResponse{ Macaroon: m, }, ) }
func (s *httpSuite) TestControllerMachineAuthForHostedModel(c *gc.C) { // Create a controller machine & hosted model. const nonce = "gary" m, password := s.Factory.MakeMachineReturningPassword(c, &factory.MachineParams{ Jobs: []state.MachineJob{state.JobManageModel}, Nonce: nonce, }) hostedState := s.Factory.MakeModel(c, nil) defer hostedState.Close() // Connect to the hosted model using the credentials of the // controller machine. apiInfo := s.APIInfo(c) apiInfo.Tag = m.Tag() apiInfo.Password = password apiInfo.ModelTag = hostedState.ModelTag() apiInfo.Nonce = nonce conn, err := api.Open(apiInfo, api.DialOpts{}) c.Assert(err, jc.ErrorIsNil) httpClient, err := conn.HTTPClient() c.Assert(err, jc.ErrorIsNil) // Test with a dummy HTTP server returns the auth related headers used. srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { username, password, ok := req.BasicAuth() if ok { httprequest.WriteJSON(w, http.StatusOK, map[string]string{ "username": username, "password": password, "nonce": req.Header.Get(params.MachineNonceHeader), }) } else { httprequest.WriteJSON(w, http.StatusUnauthorized, params.Error{ Message: "no auth header", }) } })) defer srv.Close() httpClient.BaseURL = srv.URL var out map[string]string c.Assert(httpClient.Get("/", &out), jc.ErrorIsNil) c.Assert(out, gc.DeepEquals, map[string]string{ "username": m.Tag().String(), "password": password, "nonce": nonce, }) }
func (s *ClientSuite) TestDoWithBodyAndCustomError(c *gc.C) { d := bakerytest.NewDischarger(nil, noCaveatChecker) defer d.Close() // Create a target service. svc := newService("loc", d) type customError struct { CustomError *httpbakery.Error } callCount := 0 handler := func(w http.ResponseWriter, req *http.Request) { callCount++ if _, checkErr := httpbakery.CheckRequest(svc, req, nil, checkers.New()); checkErr != nil { httprequest.WriteJSON(w, http.StatusTeapot, customError{ CustomError: newDischargeRequiredError(svc, d.Location(), nil, checkErr, req).(*httpbakery.Error), }) return } fmt.Fprintf(w, "hello there") } srv := httptest.NewServer(http.HandlerFunc(handler)) defer srv.Close() req, err := http.NewRequest("GET", srv.URL, nil) c.Assert(err, gc.IsNil) // First check that a normal request fails. resp, err := httpbakery.NewClient().Do(req) c.Assert(err, gc.IsNil) defer resp.Body.Close() c.Assert(resp.StatusCode, gc.Equals, http.StatusTeapot) c.Assert(callCount, gc.Equals, 1) callCount = 0 // Then check that a request with a custom error getter succeeds. errorGetter := func(resp *http.Response) error { if resp.StatusCode != http.StatusTeapot { return nil } data, err := ioutil.ReadAll(resp.Body) if err != nil { panic(err) } var respErr customError if err := json.Unmarshal(data, &respErr); err != nil { panic(err) } return respErr.CustomError } resp, err = httpbakery.NewClient().DoWithBodyAndCustomError(req, nil, errorGetter) c.Assert(err, gc.IsNil) data, err := ioutil.ReadAll(resp.Body) c.Assert(err, gc.IsNil) c.Assert(string(data), gc.Equals, "hello there") c.Assert(callCount, gc.Equals, 2) }
func (*handlerSuite) TestSetHeader(c *gc.C) { rec := httptest.NewRecorder() err := httprequest.WriteJSON(rec, http.StatusTeapot, HeaderNumber{1234}) c.Assert(err, gc.IsNil) c.Assert(rec.Code, gc.Equals, http.StatusTeapot) c.Assert(rec.Body.String(), gc.Equals, `{"N":1234}`) c.Assert(rec.Header().Get("content-type"), gc.Equals, "application/json") c.Assert(rec.Header().Get("some-custom-header"), gc.Equals, "yes") }
func (d *formDischarger) form(w http.ResponseWriter, r *http.Request) { r.ParseForm() if r.Form.Get("waitid") == "" { d.errorf(w, r, "no waitid") return } if r.Method == "GET" { if d.getError { httprequest.WriteJSON(w, http.StatusInternalServerError, testError) d.discharger.FinishInteraction(w, r, nil, testError) return } var sr form.SchemaResponse if !d.emptySchema { sr.Schema = environschema.Fields{ "username": environschema.Attr{ Type: environschema.Tstring, }, "password": environschema.Attr{ Type: environschema.Tstring, Secret: true, }, } } httprequest.WriteJSON(w, http.StatusOK, sr) return } if r.Method != "POST" { d.errorf(w, r, "bad method %q", r.Method) return } if d.postError { httprequest.WriteJSON(w, http.StatusInternalServerError, testError) d.discharger.FinishInteraction(w, r, nil, testError) return } var lr form.LoginRequest err := httprequest.Unmarshal(httprequest.Params{Request: r}, &lr) if err != nil { d.errorf(w, r, "bad login request: %s", err) return } d.discharger.FinishInteraction(w, r, nil, nil) }
func (*handlerSuite) TestWriteJSON(c *gc.C) { rec := httptest.NewRecorder() type Number struct { N int } err := httprequest.WriteJSON(rec, http.StatusTeapot, Number{1234}) c.Assert(err, gc.IsNil) c.Assert(rec.Code, gc.Equals, http.StatusTeapot) c.Assert(rec.Body.String(), gc.Equals, `{"N":1234}`) c.Assert(rec.Header().Get("content-type"), gc.Equals, "application/json") }
// FinishInteraction signals to the InteractiveDischarger that a // particular interaction is complete. It causes any waiting requests to // return. If err is not nil then it will be returned by the // corresponding /wait request. func (d *InteractiveDischarger) FinishInteraction(w http.ResponseWriter, r *http.Request, cavs []checkers.Caveat, err error) { r.ParseForm() d.mu.Lock() discharge, ok := d.waiting[r.Form.Get("waitid")] d.mu.Unlock() if !ok { code, body := httpbakery.ErrorToResponse(errgo.Newf("invalid waitid %q", r.Form.Get("waitid"))) httprequest.WriteJSON(w, code, body) return } select { case discharge.c <- dischargeResult{err: err, cavs: cavs}: default: panic("cannot finish interaction " + r.Form.Get("waitid")) } return }
func (s *clientSuite) TestAddLocalCharmError(c *gc.C) { client := s.APIState.Client() // AddLocalCharm does not use the facades, so instead of patching the // facade call, we set up a fake endpoint to test. defer fakeAPIEndpoint(c, client, envEndpoint(c, s.APIState, "charms"), "POST", func(w http.ResponseWriter, r *http.Request) { httprequest.WriteJSON(w, http.StatusMethodNotAllowed, ¶ms.CharmsResponse{ Error: "the POST method is not allowed", ErrorCode: params.CodeMethodNotAllowed, }) }, ).Close() charmArchive := testcharms.Repo.CharmArchive(c.MkDir(), "dummy") curl := charm.MustParseURL( fmt.Sprintf("local:quantal/%s-%d", charmArchive.Meta().Name, charmArchive.Revision()), ) _, err := client.AddLocalCharm(curl, charmArchive) c.Assert(err, gc.ErrorMatches, `POST http://.+: the POST method is not allowed`) }
client, err := s.APIState.HTTPClient() c.Assert(err, gc.IsNil) s.client = client } var httpClientTests = []struct { about string handler http.HandlerFunc expectResponse interface{} expectError string expectErrorCode string expectErrorInfo *params.ErrorInfo }{{ about: "success", handler: func(w http.ResponseWriter, req *http.Request) { httprequest.WriteJSON(w, http.StatusOK, "hello, world") }, expectResponse: newString("hello, world"), }, { about: "unauthorized status without discharge-required error", handler: func(w http.ResponseWriter, req *http.Request) { httprequest.WriteJSON(w, http.StatusUnauthorized, params.Error{ Message: "something", }) }, expectError: `GET http://.*/: something`, }, { about: "non-JSON error response", handler: http.NotFound, expectError: `GET http://.*/: unexpected content type text/plain; want application/json; content: 404 page not found`, }, {