// writeError writes an error to w in response to req. If the error was // generated because of a required macaroon that the client does not // have, we mint a macaroon that, when discharged, will grant the client // the right to execute the given operation. // // The logic in this function is crucial to the security of the service // - it must determine for a given operation what caveats to attach. func (srv *targetServiceHandler) writeError(w http.ResponseWriter, req *http.Request, operation string, verr error) { fail := func(code int, msg string, args ...interface{}) { if code == http.StatusInternalServerError { msg = "internal error: " + msg } http.Error(w, fmt.Sprintf(msg, args...), code) } if _, ok := errgo.Cause(verr).(*bakery.VerificationError); !ok { fail(http.StatusForbidden, "%v", verr) return } // Work out what caveats we need to apply for the given operation. // Could special-case the operation here if desired. caveats := []checkers.Caveat{{ Location: srv.authEndpoint, Condition: "member-of-group target-service-users", }, { Condition: "operation " + operation, }} // Mint an appropriate macaroon and send it back to the client. m, err := srv.svc.NewMacaroon("", nil, caveats) if err != nil { fail(http.StatusInternalServerError, "cannot mint macaroon: %v", err) return } httpbakery.WriteDischargeRequiredErrorForRequest(w, m, "", verr, req) }
func (s *ClientSuite) TestVersion0Generates407Status(c *gc.C) { m, err := macaroon.New([]byte("root key"), "id", "location") c.Assert(err, gc.IsNil) srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { httpbakery.WriteDischargeRequiredErrorForRequest(w, m, "", errgo.New("foo"), req) })) defer srv.Close() resp, err := http.Get(srv.URL) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusProxyAuthRequired) }
func (s *ClientSuite) TestVersion1Generates401Status(c *gc.C) { m, err := macaroon.New([]byte("root key"), "id", "location") c.Assert(err, gc.IsNil) srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { httpbakery.WriteDischargeRequiredErrorForRequest(w, m, "", errgo.New("foo"), req) })) defer srv.Close() req, err := http.NewRequest("GET", srv.URL, nil) c.Assert(err, gc.IsNil) req.Header.Set(httpbakery.BakeryProtocolHeader, "1") resp, err := http.DefaultClient.Do(req) c.Assert(err, gc.IsNil) c.Assert(resp.StatusCode, gc.Equals, http.StatusUnauthorized) c.Assert(resp.Header.Get("WWW-Authenticate"), gc.Equals, "Macaroon") }