// targetService implements a "target service", representing // an arbitrary web service that wants to delegate authorization // to third parties. func targetService(endpoint, authEndpoint string, authPK *bakery.PublicKey) (http.Handler, error) { key, err := bakery.GenerateKey() if err != nil { return nil, err } pkLocator := bakery.NewPublicKeyRing() svc, err := bakery.NewService(bakery.NewServiceParams{ Key: key, Location: endpoint, Locator: pkLocator, }) if err != nil { return nil, err } log.Printf("adding public key for location %s: %v", authEndpoint, authPK) pkLocator.AddPublicKeyForLocation(authEndpoint, true, authPK) mux := http.NewServeMux() srv := &targetServiceHandler{ svc: svc, authEndpoint: authEndpoint, } mux.HandleFunc("/gold/", srv.serveGold) mux.HandleFunc("/silver/", srv.serveSilver) return mux, nil }
func (s *suite) TestLoginDischargerError(c *gc.C) { var d *bakerytest.InteractiveDischarger d = bakerytest.NewInteractiveDischarger(nil, http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { d.FinishInteraction(w, r, nil, errors.New("test error")) }, )) defer d.Close() svc, err := bakery.NewService(bakery.NewServiceParams{ Location: "here", Locator: d, }) c.Assert(err, gc.IsNil) m, err := svc.NewMacaroon("", nil, []checkers.Caveat{{ Location: d.Location(), Condition: "something", }}) c.Assert(err, gc.IsNil) client := httpbakery.NewClient() client.VisitWebPage = func(u *url.URL) error { c.Logf("visiting %s", u) var c httprequest.Client return c.Get(u.String(), nil) } _, err = client.DischargeAll(m) c.Assert(err, gc.ErrorMatches, `cannot get discharge from ".*": failed to acquire macaroon after waiting: third party refused discharge: test error`) }
func (s *formSuite) TestFormTitle(c *gc.C) { d := &formDischarger{} d.discharger = bakerytest.NewInteractiveDischarger(nil, http.HandlerFunc(d.login)) defer d.discharger.Close() d.discharger.Mux.Handle("/form", http.HandlerFunc(d.form)) svc, err := bakery.NewService(bakery.NewServiceParams{ Locator: testLocator{ loc: d.discharger.Location(), locator: d.discharger, }, }) c.Assert(err, gc.IsNil) for i, test := range formTitleTests { c.Logf("%d. %s", i, test.host) m, err := svc.NewMacaroon("", nil, []checkers.Caveat{{ Location: "https://" + test.host, Condition: "test condition", }}) c.Assert(err, gc.Equals, nil) client := httpbakery.NewClient() client.Client.Transport = httptesting.URLRewritingTransport{ MatchPrefix: "https://" + test.host, Replace: d.discharger.Location(), RoundTripper: http.DefaultTransport, } f := new(titleTestFiller) form.SetUpAuth(client, f) ms, err := client.DischargeAll(m) c.Assert(err, gc.IsNil) c.Assert(len(ms), gc.Equals, 2) c.Assert(f.title, gc.Equals, test.expect) } }
func (s *formSuite) TestFormLogin(c *gc.C) { d := &formDischarger{} d.discharger = bakerytest.NewInteractiveDischarger(nil, http.HandlerFunc(d.login)) defer d.discharger.Close() d.discharger.Mux.Handle("/form", http.HandlerFunc(d.form)) svc, err := bakery.NewService(bakery.NewServiceParams{ Locator: d.discharger, }) c.Assert(err, gc.IsNil) for i, test := range formLoginTests { c.Logf("%d. %s", i, test.about) d.dischargeOptions = test.opts m, err := svc.NewMacaroon("", nil, []checkers.Caveat{{ Location: d.discharger.Location(), Condition: "test condition", }}) c.Assert(err, gc.Equals, nil) client := httpbakery.NewClient() h := defaultFiller if test.filler != nil { h = test.filler } client.VisitWebPage = test.fallback form.SetUpAuth(client, h) ms, err := client.DischargeAll(m) if test.expectError != "" { c.Assert(err, gc.ErrorMatches, test.expectError) continue } c.Assert(err, gc.IsNil) c.Assert(len(ms), gc.Equals, 2) } }
// NewDischarger returns a new third party caveat discharger // which uses the given function to check caveats. // The cond and arg arguments to the function are as returned // by checkers.ParseCaveat. // // If locator is non-nil, it will be used to find public keys // for any third party caveats returned by the checker. // // Calling this function has the side-effect of setting // InsecureSkipVerify in http.DefaultTransport.TLSClientConfig // until all the dischargers are closed. func NewDischarger( locator bakery.PublicKeyLocator, checker func(req *http.Request, cond, arg string) ([]checkers.Caveat, error), ) *Discharger { mux := http.NewServeMux() server := httptest.NewTLSServer(mux) svc, err := bakery.NewService(bakery.NewServiceParams{ Location: server.URL, Locator: locator, }) if err != nil { panic(err) } checker1 := func(req *http.Request, cavId, cav string) ([]checkers.Caveat, error) { cond, arg, err := checkers.ParseCaveat(cav) if err != nil { return nil, err } return checker(req, cond, arg) } httpbakery.AddDischargeHandler(mux, "/", svc, checker1) startSkipVerify() return &Discharger{ Service: svc, server: server, } }
func (s *suite) TestInteractiveDischargerURL(c *gc.C) { var d *bakerytest.InteractiveDischarger d = bakerytest.NewInteractiveDischarger(nil, http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, d.URL("/redirect", r), http.StatusFound) }, )) defer d.Close() d.Mux.Handle("/redirect", http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { d.FinishInteraction(w, r, nil, nil) }, )) svc, err := bakery.NewService(bakery.NewServiceParams{ Location: "here", Locator: d, }) c.Assert(err, gc.IsNil) m, err := svc.NewMacaroon("", nil, []checkers.Caveat{{ Location: d.Location(), Condition: "something", }}) c.Assert(err, gc.IsNil) client := httpbakery.NewClient() client.VisitWebPage = func(u *url.URL) error { var c httprequest.Client return c.Get(u.String(), nil) } ms, err := client.DischargeAll(m) c.Assert(err, gc.IsNil) c.Assert(ms, gc.HasLen, 2) err = svc.Check(ms, failChecker) c.Assert(err, gc.IsNil) }
func (*suite) TestMacaraq(c *gc.C) { checked := false d := bakerytest.NewDischarger(nil, func(_ *http.Request, cond, arg string) ([]checkers.Caveat, error) { if cond != "something" { return nil, fmt.Errorf("unexpected 3rd party cond") } checked = true return nil, nil }) bsvc, err := bakery.NewService(bakery.NewServiceParams{ Location: "here", Locator: d, }) c.Assert(err, gc.IsNil) svc := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { req.ParseForm() _, checkErr := httpbakery.CheckRequest(bsvc, req, nil, checkers.New()) if checkErr == nil { w.Header().Set("Content-Type", "application/json") data, err := json.Marshal(req.Form) c.Check(err, gc.IsNil) w.Write(data) return } m, err := bsvc.NewMacaroon("", nil, []checkers.Caveat{{ Location: d.Service.Location(), Condition: "something", }}) c.Check(err, gc.IsNil) httpbakery.WriteDischargeRequiredError(w, m, "/", checkErr) })) fset := flag.NewFlagSet("http", flag.ContinueOnError) ctxt, params, err := newContext(fset, []string{ svc.URL, "x=y", }) c.Assert(err, gc.IsNil) client := httpbakery.NewClient() resp, err := ctxt.doRequest(client, nil) c.Assert(err, gc.IsNil) defer resp.Body.Close() c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) c.Assert(checked, jc.IsTrue) var stdout bytes.Buffer err = showResponse(params, resp, &stdout) c.Assert(err, gc.IsNil) c.Assert(stdout.String(), gc.Equals, `{ x: [ "y" ] } `) }
func newService(location string, locator bakery.PublicKeyLocator) *bakery.Service { svc, err := bakery.NewService(bakery.NewServiceParams{ Location: location, Locator: locator, }) if err != nil { panic(err) } return svc }
func (s *agentSuite) SetUpSuite(c *gc.C) { key, err := bakery.GenerateKey() c.Assert(err, gc.IsNil) s.dischargeKey = &key.Public c.Assert(err, gc.IsNil) bak, err := bakery.NewService(bakery.NewServiceParams{ Key: key, }) c.Assert(err, gc.IsNil) s.discharger = &Discharger{ Bakery: bak, } s.server = s.discharger.Serve() s.bakery, err = bakery.NewService(bakery.NewServiceParams{ Locator: bakery.PublicKeyLocatorMap{ s.discharger.URL: &key.Public, }, }) }
func (ctx condContext) addThirdPartyCaveat(m *macaroon.Macaroon, location, condition string) error { agent, err := bakery.NewService(bakery.NewServiceParams{ // TODO: persistent key pair for client Locator: ctx, }) if err != nil { return err } return agent.AddCaveat(m, checkers.Caveat{Location: location, Condition: condition}) }
// authService implements an authorization service, // that can discharge third-party caveats added // to other macaroons. func authService(endpoint string, key *bakery.KeyPair) (http.Handler, error) { svc, err := bakery.NewService(bakery.NewServiceParams{ Location: endpoint, Key: key, Locator: bakery.NewPublicKeyRing(), }) if err != nil { return nil, err } mux := http.NewServeMux() httpbakery.AddDischargeHandler(mux, "/", svc, thirdPartyChecker) return mux, nil }
// ExpireStorageAt implements authentication.ExpirableStorageBakeryService. func (s *expirableStorageBakeryService) ExpireStorageAt(t time.Time) (authentication.ExpirableStorageBakeryService, error) { store := s.store.ExpireAt(t) service, err := bakery.NewService(bakery.NewServiceParams{ Location: s.Location(), Store: store, Key: s.key, Locator: s.locator, }) if err != nil { return nil, errors.Trace(err) } return &expirableStorageBakeryService{service, s.key, store, s.locator}, nil }
func (s *suite) TestDischargerTwoLevels(c *gc.C) { d1checker := func(_ *http.Request, cond, arg string) ([]checkers.Caveat, error) { if cond != "xtrue" { return nil, fmt.Errorf("caveat refused") } return nil, nil } d1 := bakerytest.NewDischarger(nil, d1checker) defer d1.Close() d2checker := func(_ *http.Request, cond, arg string) ([]checkers.Caveat, error) { return []checkers.Caveat{{ Location: d1.Location(), Condition: "x" + cond, }}, nil } d2 := bakerytest.NewDischarger(d1, d2checker) defer d2.Close() locator := bakery.PublicKeyLocatorMap{ d1.Location(): d1.Service.PublicKey(), d2.Location(): d2.Service.PublicKey(), } c.Logf("map: %s", locator) svc, err := bakery.NewService(bakery.NewServiceParams{ Location: "here", Locator: locator, }) c.Assert(err, gc.IsNil) m, err := svc.NewMacaroon("", nil, []checkers.Caveat{{ Location: d2.Location(), Condition: "true", }}) c.Assert(err, gc.IsNil) ms, err := s.client.DischargeAll(m) c.Assert(err, gc.IsNil) c.Assert(ms, gc.HasLen, 3) err = svc.Check(ms, failChecker) c.Assert(err, gc.IsNil) err = svc.AddCaveat(m, checkers.Caveat{ Location: d2.Location(), Condition: "nope", }) c.Assert(err, gc.IsNil) ms, err = s.client.DischargeAll(m) c.Assert(err, gc.ErrorMatches, `cannot get discharge from "https://[^"]*": third party refused discharge: cannot discharge: caveat refused`) c.Assert(ms, gc.HasLen, 0) }
func newService(c *gc.C, location string, locator bakery.PublicKeyLocatorMap) *bakery.Service { keyPair, err := bakery.GenerateKey() c.Assert(err, gc.IsNil) svc, err := bakery.NewService(bakery.NewServiceParams{ Location: location, Key: keyPair, Locator: locator, }) c.Assert(err, gc.IsNil) if locator != nil { locator[location] = &keyPair.Public } return svc }
func newMockAPI() (*mockapi, error) { kp, err := bakery.GenerateKey() if err != nil { return nil, errors.Trace(err) } svc, err := bakery.NewService(bakery.NewServiceParams{ Location: "omnibus", Key: kp, }) if err != nil { return nil, errors.Trace(err) } return &mockapi{ service: svc, }, nil }
func (s *macaroonAuthenticatorSuite) TestMacaroonAuthentication(c *gc.C) { discharger := bakerytest.NewDischarger(nil, s.Checker) defer discharger.Close() for i, test := range authenticateSuccessTests { c.Logf("\ntest %d; %s", i, test.about) s.username = test.dischargedUsername svc, err := bakery.NewService(bakery.NewServiceParams{ Locator: discharger, }) c.Assert(err, jc.ErrorIsNil) mac, err := svc.NewMacaroon("", nil, nil) c.Assert(err, jc.ErrorIsNil) authenticator := &authentication.ExternalMacaroonAuthenticator{ Service: svc, IdentityLocation: discharger.Location(), Macaroon: mac, } // Authenticate once to obtain the macaroon to be discharged. _, err = authenticator.Authenticate(test.finder, nil, params.LoginRequest{ Credentials: "", Nonce: "", Macaroons: nil, }) // Discharge the macaroon. dischargeErr := errors.Cause(err).(*common.DischargeRequiredError) client := httpbakery.NewClient() ms, err := client.DischargeAll(dischargeErr.Macaroon) c.Assert(err, jc.ErrorIsNil) // Authenticate again with the discharged macaroon. entity, err := authenticator.Authenticate(test.finder, nil, params.LoginRequest{ Credentials: "", Nonce: "", Macaroons: []macaroon.Slice{ms}, }) if test.expectError != "" { c.Assert(err, gc.ErrorMatches, test.expectError) c.Assert(entity, gc.Equals, nil) } else { c.Assert(err, jc.ErrorIsNil) c.Assert(entity.Tag().String(), gc.Equals, test.expectTag) } } }
// NewService returns a new Service instance. func NewService(config ServiceConfig) (*Service, error) { bakeryService, err := bakery.NewService(bakery.NewServiceParams{}) if err != nil { return nil, errgo.Mask(err, errgo.Any) } s := &Service{bakery: bakeryService, prefix: config.Prefix} s.mux = http.NewServeMux() httpbakery.AddDischargeHandler(s.mux, config.Prefix+"/discharger", s.bakery, s.checker) r := httprouter.New() r.GET(config.Prefix+"/wait/:election", s.wait) r.GET(config.Prefix+"/approve/:ballot", s.approve) r.GET(config.Prefix+"/deny/:ballot", s.deny) s.mux.Handle("/", r) return s, nil }
func (s *ServiceSuite) TestNewMacaroonWithRootKeyStorage(c *gc.C) { svc, err := bakery.NewService(bakery.NewServiceParams{ Location: "somewhere", }) c.Assert(err, gc.IsNil) store := bakery.NewMemRootKeyStorage() key, id, err := store.RootKey() c.Assert(err, gc.IsNil) svc = svc.WithRootKeyStore(store) m, err := svc.NewMacaroon("", nil, []checkers.Caveat{{ Location: "", Condition: "something", }}) c.Assert(err, gc.IsNil) c.Assert(m.Location(), gc.Equals, "somewhere") id1 := m.Id() c.Assert(id1, gc.Matches, id+"-[0-9a-f]{32}") err = svc.Check(macaroon.Slice{m}, strcmpChecker("something")) c.Assert(err, gc.IsNil) // Check that it's really using the root key returned from // the store. err = m.Verify(key, func(string) error { return nil }, nil) c.Assert(err, gc.IsNil) // Create another one and check that it re-uses the // same key but has a different id. m, err = svc.NewMacaroon("", nil, []checkers.Caveat{{ Location: "", Condition: "something", }}) c.Assert(err, gc.IsNil) c.Assert(m.Location(), gc.Equals, "somewhere") id2 := m.Id() c.Assert(id2, gc.Matches, id+"-[0-9a-f]{32}") c.Assert(id2, gc.Not(gc.Equals), id1) err = m.Verify(key, func(string) error { return nil }, nil) c.Assert(err, gc.IsNil) }
func (*DischargeSuite) TestDischargeAllLocalDischarge(c *gc.C) { svc, err := bakery.NewService(bakery.NewServiceParams{}) c.Assert(err, gc.IsNil) clientKey, err := bakery.GenerateKey() c.Assert(err, gc.IsNil) m, err := svc.NewMacaroon("", nil, []checkers.Caveat{ bakery.LocalThirdPartyCaveat(&clientKey.Public), }) c.Assert(err, gc.IsNil) ms, err := bakery.DischargeAllWithKey(m, noDischarge(c), clientKey) c.Assert(err, gc.IsNil) err = svc.Check(ms, checkers.New()) c.Assert(err, gc.IsNil) }
func (s *ServiceSuite) TestNewMacaroonWithExplicitIdAndRootKeyStorage(c *gc.C) { store := bakery.NewMemRootKeyStorage() // Check that we can create a bakery with the root key store // in its parameters too. svc, err := bakery.NewService(bakery.NewServiceParams{ Location: "somewhere", RootKeyStore: store, }) c.Assert(err, gc.IsNil) m, err := svc.NewMacaroon("someid", nil, nil) c.Assert(err, gc.ErrorMatches, `cannot choose root key or id when using RootKeyStore`) c.Assert(m, gc.IsNil) m, err = svc.NewMacaroon("", []byte{1}, nil) c.Assert(err, gc.ErrorMatches, `cannot choose root key or id when using RootKeyStore`) c.Assert(m, gc.IsNil) }
func (ctx newContext) addThirdPartyCaveat(m *macaroon.Macaroon, env *envelope) error { condition, err := env.MarshalJSON() if err != nil { return err } mgr := keyManager{ctx.Context} kp, err := mgr.keyPair() if err != nil { return err } agent, err := bakery.NewService(bakery.NewServiceParams{ Key: kp.KeyPair, Locator: clientLocator{ctx.Context, kp}, }) if err != nil { return err } return agent.AddCaveat(m, checkers.Caveat{Location: "client:encrypt", Condition: string(condition)}) }
// New returns a new handler that services an identity-providing // service. This acts as a login service and can discharge third-party caveats // for users. func New(p Params) (http.Handler, error) { svc, err := bakery.NewService(p.Service) if err != nil { return nil, err } h := &handler{ svc: svc, users: p.Users, place: &place{meeting.New()}, } mux := http.NewServeMux() httpbakery.AddDischargeHandler(mux, "/", svc, h.checkThirdPartyCaveat) mux.Handle("/user/", mkHandler(handleJSON(h.userHandler))) mux.HandleFunc("/login", h.loginHandler) mux.Handle("/question", mkHandler(handleJSON(h.questionHandler))) mux.Handle("/wait", mkHandler(handleJSON(h.waitHandler))) mux.HandleFunc("/loginattempt", h.loginAttemptHandler) return mux, nil }
func (s *BakeryStorageSuite) initService(c *gc.C, enableExpiry bool) { store, err := bakerystorage.New(bakerystorage.Config{ GetCollection: func() (mongo.Collection, func()) { return mongo.CollectionFromName(s.db, s.coll.Name) }, }) c.Assert(err, jc.ErrorIsNil) if enableExpiry { store = store.ExpireAt(time.Now()) } s.store = store service, err := bakery.NewService(bakery.NewServiceParams{ Location: "straya", Store: s.store, }) c.Assert(err, jc.ErrorIsNil) s.service = service }
// newBakeryService creates a new bakery.Service. func newBakeryService( st *state.State, store bakerystorage.ExpirableStorage, locator bakery.PublicKeyLocator, ) (*bakery.Service, *bakery.KeyPair, error) { key, err := bakery.GenerateKey() if err != nil { return nil, nil, errors.Annotate(err, "generating key for bakery service") } service, err := bakery.NewService(bakery.NewServiceParams{ Location: "juju model " + st.ModelUUID(), Store: store, Key: key, Locator: locator, }) if err != nil { return nil, nil, errors.Trace(err) } return service, key, nil }
func (s *ServiceSuite) TestNewMacaroonWithRootKeyStorageInParams(c *gc.C) { store := bakery.NewMemRootKeyStorage() _, id, err := store.RootKey() c.Assert(err, gc.IsNil) // Check that we can create a bakery with the root key store // in its parameters too. svc, err := bakery.NewService(bakery.NewServiceParams{ Location: "elsewhere", RootKeyStore: store, }) c.Assert(err, gc.IsNil) m, err := svc.NewMacaroon("", nil, nil) c.Assert(err, gc.IsNil) c.Assert(m.Id(), gc.Matches, id+"-[0-9a-f]{32}") err = svc.Check(macaroon.Slice{m}, checkers.New()) c.Assert(err, gc.IsNil) }
func (s *formSuite) TestFormLogin(c *gc.C) { d := &formDischarger{} d.discharger = bakerytest.NewInteractiveDischarger(nil, http.HandlerFunc(d.visit)) defer d.discharger.Close() d.discharger.Mux.Handle("/form", http.HandlerFunc(d.form)) svc, err := bakery.NewService(bakery.NewServiceParams{ Locator: d.discharger, }) c.Assert(err, gc.IsNil) for i, test := range formLoginTests { c.Logf("test %d: %s", i, test.about) d.dischargeOptions = test.opts m, err := svc.NewMacaroon("", nil, []checkers.Caveat{{ Location: d.discharger.Location(), Condition: "test condition", }}) c.Assert(err, gc.Equals, nil) client := httpbakery.NewClient() filler := defaultFiller if test.filler != nil { filler = test.filler } handlers := []httpbakery.Visitor{ form.Visitor{ Filler: filler, }, } if test.fallback != nil { handlers = append(handlers, test.fallback) } client.WebPageVisitor = httpbakery.NewMultiVisitor(handlers...) ms, err := client.DischargeAll(m) if test.expectError != "" { c.Assert(err, gc.ErrorMatches, test.expectError) continue } c.Assert(err, gc.IsNil) c.Assert(len(ms), gc.Equals, 2) } }
func (s *suite) TestDischargerSimple(c *gc.C) { d := bakerytest.NewDischarger(nil, noCaveatChecker) defer d.Close() svc, err := bakery.NewService(bakery.NewServiceParams{ Location: "here", Locator: d, }) c.Assert(err, gc.IsNil) m, err := svc.NewMacaroon("", nil, []checkers.Caveat{{ Location: d.Location(), Condition: "something", }}) c.Assert(err, gc.IsNil) ms, err := s.client.DischargeAll(m) c.Assert(err, gc.IsNil) c.Assert(ms, gc.HasLen, 2) err = svc.Check(ms, failChecker) c.Assert(err, gc.IsNil) }
// NewService creates a new opaque object storage service. func NewService(config ServiceConfig) (*Service, error) { bakeryService, err := bakery.NewService(bakery.NewServiceParams{ Store: config.BakeryStore, }) if err != nil { return nil, err } s := &Service{ bakery: bakeryService, store: config.ObjectStore, } prefix := "/" if config.Prefix != "" { prefix = config.Prefix } s.router = httprouter.New() s.router.POST(prefix, s.create) s.router.POST(path.Join(prefix, ":object"), s.fetch) s.router.DELETE(path.Join(prefix, ":object"), s.del) return s, nil }
// newMacaroonAuth returns an authenticator that can authenticate // macaroon-based logins. This is just a helper function for authCtxt.macaroonAuth. func newMacaroonAuth(st *state.State) (*authentication.MacaroonAuthenticator, error) { envCfg, err := st.EnvironConfig() if err != nil { return nil, errors.Annotate(err, "cannot get environment config") } idURL := envCfg.IdentityURL() if idURL == "" { return nil, errMacaroonAuthNotConfigured } // The identity server has been configured, // so configure the bakery service appropriately. idPK := envCfg.IdentityPublicKey() if idPK == nil { // No public key supplied - retrieve it from the identity manager. idPK, err = httpbakery.PublicKeyForLocation(http.DefaultClient, idURL) if err != nil { return nil, errors.Annotate(err, "cannot get identity public key") } } svc, err := bakery.NewService( bakery.NewServiceParams{ Location: "juju environment " + st.EnvironUUID(), Locator: bakery.PublicKeyLocatorMap{ idURL: idPK, }, }, ) if err != nil { return nil, errors.Annotate(err, "cannot make bakery service") } var auth authentication.MacaroonAuthenticator auth.Service = svc auth.Macaroon, err = svc.NewMacaroon("api-login", nil, nil) if err != nil { return nil, errors.Annotate(err, "cannot make macaroon") } auth.IdentityLocation = idURL return &auth, nil }
func (s *suite) TestInteractiveDischarger(c *gc.C) { var d *bakerytest.InteractiveDischarger d = bakerytest.NewInteractiveDischarger(nil, http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { d.FinishInteraction(w, r, []checkers.Caveat{ checkers.Caveat{ Condition: "test pass", }, }, nil) }, )) defer d.Close() svc, err := bakery.NewService(bakery.NewServiceParams{ Location: "here", Locator: d, }) c.Assert(err, gc.IsNil) m, err := svc.NewMacaroon("", nil, []checkers.Caveat{{ Location: d.Location(), Condition: "something", }}) c.Assert(err, gc.IsNil) client := httpbakery.NewClient() client.VisitWebPage = func(u *url.URL) error { var c httprequest.Client return c.Get(u.String(), nil) } ms, err := client.DischargeAll(m) c.Assert(err, gc.IsNil) c.Assert(ms, gc.HasLen, 2) var r recordingChecker err = svc.Check(ms, &r) c.Assert(err, gc.IsNil) c.Assert(r.caveats, gc.HasLen, 1) c.Assert(r.caveats[0], gc.Equals, "test pass") }