func (d *dischargeHandler) serveDischarge1(p httprequest.Params) (interface{}, error) { logger.Debugf("dischargeHandler.serveDischarge {") defer logger.Debugf("}") if p.Request.Method != "POST" { // TODO http.StatusMethodNotAllowed) return nil, badRequestErrorf("method not allowed") } p.Request.ParseForm() id := p.Request.Form.Get("id") if id == "" { return nil, badRequestErrorf("id attribute is empty") } checker := func(cavId, cav string) ([]checkers.Caveat, error) { return d.checker(p.Request, cavId, cav) } // TODO(rog) pass location into discharge // location := p.Request.Form.Get("location") var resp dischargeResponse m, err := d.svc.Discharge(bakery.ThirdPartyCheckerFunc(checker), id) if err != nil { return nil, errgo.NoteMask(err, "cannot discharge", errgo.Any) } resp.Macaroon = m return &resp, nil }
// AcquireDischarge implements httpbakery.DischargeAcquirer. func (da *dischargeAcquirer) AcquireDischarge(firstPartyLocation string, cav macaroon.Caveat) (*macaroon.Macaroon, error) { if cav.Location == "client:encrypt" { dm, _, err := bakery.Discharge(da.client.Key, bakery.ThirdPartyCheckerFunc(da.clientEncryptChecker), cav.Id) return dm, err } return da.client.AcquireDischarge(firstPartyLocation, cav) }
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 *ServiceSuite) TestDischargeMacaroonCannotBeUsedAsNormalMacaroon(c *gc.C) { locator := make(bakery.PublicKeyLocatorMap) firstParty := newService(c, "first", locator) thirdParty := newService(c, "third", locator) // First party mints a macaroon with a 3rd party caveat. m, err := firstParty.NewMacaroon("", nil, []checkers.Caveat{{ Location: "third", Condition: "true", }}) c.Assert(err, gc.IsNil) // Acquire the discharge macaroon, but don't bind it to the original. d, err := thirdParty.Discharge(bakery.ThirdPartyCheckerFunc(func(_, caveat string) ([]checkers.Caveat, error) { return nil, nil }), m.Caveats()[0].Id) c.Assert(err, gc.IsNil) // Make sure it cannot be used as a normal macaroon in the third party. err = thirdParty.Check(macaroon.Slice{d}, checkers.New()) c.Assert(err, gc.ErrorMatches, `verification failed: macaroon not found in storage`) }
func (d *Discharger) wait(w http.ResponseWriter, r *http.Request) { r.ParseForm() id, err := strconv.Atoi(r.Form.Get("waitid")) if err != nil { d.WriteJSON(w, http.StatusBadRequest, httpbakery.Error{ Message: fmt.Sprintf("cannot read waitid: %s", err), }) return } err = <-d.waiting[id].c if err != nil { d.WriteJSON(w, http.StatusForbidden, err) return } m, err := d.Bakery.Discharge( bakery.ThirdPartyCheckerFunc( func(cavId, caveat string) ([]checkers.Caveat, error) { return nil, nil }, ), d.waiting[id].cavId, ) if err != nil { d.WriteJSON(w, http.StatusForbidden, err) return } d.WriteJSON( w, http.StatusOK, struct { Macaroon *macaroon.Macaroon }{ Macaroon: m, }, ) }
func (s *ServiceSuite) TestDischargeTwoNeedDeclared(c *gc.C) { locator := make(bakery.PublicKeyLocatorMap) firstParty := newService(c, "first", locator) thirdParty := newService(c, "third", locator) // firstParty mints a macaroon with two third party caveats // with overlapping attributes. m, err := firstParty.NewMacaroon("", nil, []checkers.Caveat{ checkers.NeedDeclaredCaveat(checkers.Caveat{ Location: "third", Condition: "x", }, "foo", "bar"), checkers.NeedDeclaredCaveat(checkers.Caveat{ Location: "third", Condition: "y", }, "bar", "baz"), }) c.Assert(err, gc.IsNil) // The client asks for a discharge macaroon for each third party caveat. // Since no declarations are added by the discharger, d, err := bakery.DischargeAll(m, func(_ string, cav macaroon.Caveat) (*macaroon.Macaroon, error) { return thirdParty.Discharge(bakery.ThirdPartyCheckerFunc(func(_, caveat string) ([]checkers.Caveat, error) { return nil, nil }), cav.Id) }) c.Assert(err, gc.IsNil) declared := checkers.InferDeclared(d) c.Assert(declared, gc.DeepEquals, checkers.Declared{ "foo": "", "bar": "", "baz": "", }) err = firstParty.Check(d, checkers.New(declared)) c.Assert(err, gc.IsNil) // If they return conflicting values, the discharge fails. // The client asks for a discharge macaroon for each third party caveat. // Since no declarations are added by the discharger, d, err = bakery.DischargeAll(m, func(_ string, cav macaroon.Caveat) (*macaroon.Macaroon, error) { return thirdParty.Discharge(bakery.ThirdPartyCheckerFunc(func(_, caveat string) ([]checkers.Caveat, error) { switch caveat { case "x": return []checkers.Caveat{ checkers.DeclaredCaveat("foo", "fooval1"), }, nil case "y": return []checkers.Caveat{ checkers.DeclaredCaveat("foo", "fooval2"), checkers.DeclaredCaveat("baz", "bazval"), }, nil } return nil, fmt.Errorf("not matched") }), cav.Id) }) c.Assert(err, gc.IsNil) declared = checkers.InferDeclared(d) c.Assert(declared, gc.DeepEquals, checkers.Declared{ "bar": "", "baz": "bazval", }) err = firstParty.Check(d, checkers.New(declared)) c.Assert(err, gc.ErrorMatches, `verification failed: caveat "declared foo fooval1" not satisfied: got foo=null, expected "fooval1"`) }