func (s *ClientSuite) TestSingleServiceFirstPartyWithHeader(c *gc.C) { // Create a target service. svc := newService("loc", nil) // No discharge required, so pass "unknown" for the third party // caveat discharger location so we know that we don't try // to discharge the location. ts := httptest.NewServer(serverHandler(svc, "unknown", nil)) defer ts.Close() // Mint a macaroon for the target service. serverMacaroon, err := svc.NewMacaroon("", nil, nil) c.Assert(err, gc.IsNil) c.Assert(serverMacaroon.Location(), gc.Equals, "loc") err = svc.AddCaveat(serverMacaroon, checkers.Caveat{ Condition: "is something", }) c.Assert(err, gc.IsNil) // Serialize the macaroon slice. data, err := json.Marshal(macaroon.Slice{serverMacaroon}) c.Assert(err, gc.IsNil) value := base64.StdEncoding.EncodeToString(data) // Create a client request. req, err := http.NewRequest("GET", ts.URL, nil) c.Assert(err, gc.IsNil) req.Header.Set(httpbakery.MacaroonsHeader, value) client := httpbakery.NewHTTPClient() // Make the request to the server. resp, err := client.Do(req) c.Assert(err, gc.IsNil) defer resp.Body.Close() assertResponse(c, resp, "done") }
func clientRequestWithCookies(c *gc.C, u string, macaroons macaroon.Slice) *http.Client { client := httpbakery.NewHTTPClient() url, err := url.Parse(u) c.Assert(err, gc.IsNil) err = httpbakery.SetCookie(client.Jar, url, macaroons) c.Assert(err, gc.IsNil) return client }
func newClient() *httpbakery.Client { return &httpbakery.Client{ Client: httpbakery.NewHTTPClient(), VisitWebPage: func(url *url.URL) error { fmt.Printf("please visit this web page:\n") fmt.Printf("\t%s\n", url) return nil }, } }
func (s *KeyringSuite) TestPublicKeyReturnsStatusInternalServerError(c *gc.C) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) })) defer ts.Close() client := httpbakery.NewHTTPClient() _, err := httpbakery.PublicKeyForLocation(client, ts.URL) c.Assert(err, gc.ErrorMatches, fmt.Sprintf(`cannot get public key from "%s/publickey": got status 500 Internal Server Error`, ts.URL)) }
func (s *KeyringSuite) TestPublicKeyReturnsInvalidJSON(c *gc.C) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "BADJSON") })) defer ts.Close() client := httpbakery.NewHTTPClient() _, err := httpbakery.PublicKeyForLocation(client, ts.URL) c.Assert(err, gc.ErrorMatches, fmt.Sprintf(`failed to decode response from "%s/publickey": invalid character 'B' looking for beginning of value`, ts.URL)) }
func badResponseClient(resp *http.Response, err error) *csclient.Client { client := httpbakery.NewHTTPClient() client.Transport = &cannedRoundTripper{ resp: resp, error: err, } return csclient.New(csclient.Params{ URL: "http://0.1.2.3", User: "******", HTTPClient: client, }) }
func (s *suite) TestLogin(c *gc.C) { ch := charmRepo.CharmDir("wordpress") url := charm.MustParseReference("~charmers/utopic/wordpress-42") purl := charm.MustParseReference("utopic/wordpress-42") err := s.client.UploadCharmWithRevision(url, ch, 42) c.Assert(err, gc.IsNil) err = s.client.Put("/"+url.Path()+"/meta/perm/read", []string{"bob"}) c.Assert(err, gc.IsNil) httpClient := httpbakery.NewHTTPClient() client := csclient.New(csclient.Params{ URL: s.srv.URL, HTTPClient: httpClient, }) var result struct{ IdRevision struct{ Revision int } } _, err = client.Meta(purl, &result) c.Assert(err, gc.NotNil) // Try logging in when the discharger fails. err = client.Login() c.Assert(err, gc.ErrorMatches, `cannot retrieve the authentication macaroon: cannot get discharge from ".*": third party refused discharge: cannot discharge: no discharge`) // Allow the discharge. s.discharge = func(cond, arg string) ([]checkers.Caveat, error) { return []checkers.Caveat{checkers.DeclaredCaveat("username", "bob")}, nil } err = client.Login() c.Assert(err, gc.IsNil) // Change discharge so that we're sure the cookies are being // used rather than the discharge mechanism. s.discharge = func(cond, arg string) ([]checkers.Caveat, error) { return nil, fmt.Errorf("no discharge") } // Check that the request still works. _, err = client.Meta(purl, &result) c.Assert(err, gc.IsNil) c.Assert(result.IdRevision.Revision, gc.Equals, url.Revision) // Check that we've got one cookie. srvURL, err := neturl.Parse(s.srv.URL) c.Assert(err, gc.IsNil) c.Assert(httpClient.Jar.Cookies(srvURL), gc.HasLen, 1) // Log in again. err = client.Login() c.Assert(err, gc.IsNil) // Check that we still only have one cookie. c.Assert(httpClient.Jar.Cookies(srvURL), gc.HasLen, 1) }
func (s *KeyringSuite) TestPublicKey(c *gc.C) { d := bakerytest.NewDischarger(nil, noCaveatChecker) defer d.Close() client := httpbakery.NewHTTPClient() publicKey, err := httpbakery.PublicKeyForLocation(client, d.Location()) c.Assert(err, gc.IsNil) expectedKey := d.Service.PublicKey() c.Assert(publicKey, gc.DeepEquals, expectedKey) // Check that it works with client==nil. publicKey, err = httpbakery.PublicKeyForLocation(nil, d.Location()) c.Assert(err, gc.IsNil) c.Assert(publicKey, gc.DeepEquals, expectedKey) }
func newHTTPClient() (*cookiejar.Jar, *http.Client, error) { cookieFile := path.Join(utils.Home(), ".go-cookies") jar, err := cookiejar.New(&cookiejar.Options{ PublicSuffixList: publicsuffix.List, }) if err != nil { panic(err) } if err := jar.Load(cookieFile); err != nil { return nil, nil, err } client := httpbakery.NewHTTPClient() client.Jar = jar return jar, client, nil }
// New returns a new charm store client. func New(p Params) *Client { if p.URL == "" { p.URL = ServerURL } if p.HTTPClient == nil { p.HTTPClient = httpbakery.NewHTTPClient() } return &Client{ params: p, bclient: &httpbakery.Client{ Client: p.HTTPClient, VisitWebPage: p.VisitWebPage, }, } }
func (s *suite) TestWhoAmI(c *gc.C) { httpClient := httpbakery.NewHTTPClient() client := csclient.New(csclient.Params{ URL: s.srv.URL, HTTPClient: httpClient, }) response, err := client.WhoAmI() c.Assert(err, gc.ErrorMatches, `cannot retrieve whoami response: cannot get discharge from ".*": third party refused discharge: cannot discharge: no discharge`) s.discharge = func(cond, arg string) ([]checkers.Caveat, error) { return []checkers.Caveat{checkers.DeclaredCaveat("username", "bob")}, nil } response, err = client.WhoAmI() c.Assert(err, gc.IsNil) c.Assert(response.User, gc.Equals, "bob") }
func newCachingClient( cache MacaroonCache, server *url.URL, makeWrapper func(*httpbakery.Client, *url.URL) csWrapper, ) (Client, error) { bakeryClient := &httpbakery.Client{ Client: httpbakery.NewHTTPClient(), } client := makeWrapper(bakeryClient, server) server, err := url.Parse(client.ServerURL()) if err != nil { return Client{}, errors.Trace(err) } jar, err := newMacaroonJar(cache, server) if err != nil { return Client{}, errors.Trace(err) } bakeryClient.Jar = jar return Client{client, jar}, nil }
func openCSClient(args params.AddCharmWithAuthorization) (*csclient.Client, error) { csURL, err := url.Parse(csclient.ServerURL) if err != nil { return nil, err } csParams := csclient.Params{ URL: csURL.String(), HTTPClient: httpbakery.NewHTTPClient(), } if args.CharmStoreMacaroon != nil { // Set the provided charmstore authorizing macaroon // as a cookie in the HTTP client. // TODO(cmars) discharge any third party caveats in the macaroon. ms := []*macaroon.Macaroon{args.CharmStoreMacaroon} httpbakery.SetCookie(csParams.HTTPClient.Jar, csURL, ms) } csClient := csclient.New(csParams) channel := csparams.Channel(args.Channel) if channel != csparams.NoChannel { csClient = csClient.WithChannel(channel) } return csClient, nil }
// AddCharmWithAuthorization adds the given charm URL (which must include revision) to // the environment, if it does not exist yet. Local charms are not // supported, only charm store URLs. See also AddLocalCharm(). // // The authorization macaroon, args.CharmStoreMacaroon, may be // omitted, in which case this call is equivalent to AddCharm. func AddCharmWithAuthorization(st *state.State, args params.AddCharmWithAuthorization) error { charmURL, err := charm.ParseURL(args.URL) if err != nil { return err } if charmURL.Schema != "cs" { return fmt.Errorf("only charm store charm URLs are supported, with cs: schema") } if charmURL.Revision < 0 { return fmt.Errorf("charm URL must include revision") } // First, check if a pending or a real charm exists in state. stateCharm, err := st.PrepareStoreCharmUpload(charmURL) if err != nil { return err } if stateCharm.IsUploaded() { // Charm already in state (it was uploaded already). return nil } // Get the charm and its information from the store. envConfig, err := st.EnvironConfig() if err != nil { return err } csURL, err := url.Parse(csclient.ServerURL) if err != nil { return err } csParams := charmrepo.NewCharmStoreParams{ URL: csURL.String(), HTTPClient: httpbakery.NewHTTPClient(), } if args.CharmStoreMacaroon != nil { // Set the provided charmstore authorizing macaroon // as a cookie in the HTTP client. // TODO discharge any third party caveats in the macaroon. ms := []*macaroon.Macaroon{args.CharmStoreMacaroon} httpbakery.SetCookie(csParams.HTTPClient.Jar, csURL, ms) } repo := config.SpecializeCharmRepo( NewCharmStore(csParams), envConfig, ) downloadedCharm, err := repo.Get(charmURL) if err != nil { cause := errors.Cause(err) if httpbakery.IsDischargeError(cause) || httpbakery.IsInteractionError(cause) { return errors.NewUnauthorized(err, "") } return errors.Trace(err) } // Open it and calculate the SHA256 hash. downloadedBundle, ok := downloadedCharm.(*charm.CharmArchive) if !ok { return errors.Errorf("expected a charm archive, got %T", downloadedCharm) } archive, err := os.Open(downloadedBundle.Path) if err != nil { return errors.Annotate(err, "cannot read downloaded charm") } defer archive.Close() bundleSHA256, size, err := utils.ReadSHA256(archive) if err != nil { return errors.Annotate(err, "cannot calculate SHA256 hash of charm") } if _, err := archive.Seek(0, 0); err != nil { return errors.Annotate(err, "cannot rewind charm archive") } // Store the charm archive in environment storage. return StoreCharmArchive( st, charmURL, downloadedCharm, archive, size, bundleSHA256, ) }
// could also be a web browser. // (TODO: write javascript discharge gatherer) package main import ( "fmt" "log" "net" "net/http" "net/url" "gopkg.in/macaroon-bakery.v1/bakery" "gopkg.in/macaroon-bakery.v1/httpbakery" ) var defaultHTTPClient = httpbakery.NewHTTPClient() func main() { key, err := bakery.GenerateKey() if err != nil { log.Fatalf("cannot generate auth service key pair: %v", err) } authPublicKey := &key.Public authEndpoint := mustServe(func(endpoint string) (http.Handler, error) { return authService(endpoint, key) }) serverEndpoint := mustServe(func(endpoint string) (http.Handler, error) { return targetService(endpoint, authEndpoint, authPublicKey) }) resp, err := clientRequest(newClient(), serverEndpoint) if err != nil {
func (s *KeyringSuite) TestPublicKeyWrongURL(c *gc.C) { client := httpbakery.NewHTTPClient() _, err := httpbakery.PublicKeyForLocation(client, "http://localhost:0") c.Assert(err, gc.ErrorMatches, `cannot get public key from "http://localhost:0/publickey": Get http://localhost:0/publickey: dial tcp 127.0.0.1:0: .*connection refused`) }