Exemple #1
0
// CreateLocalLoginMacaroon creates a time-limited macaroon for a local user
// to log into the controller with. The macaroon will be valid for use with
// UserAuthenticator.Authenticate until the time limit expires, or the Juju
// controller agent restarts.
//
// NOTE(axw) this method will generate a key for a previously unseen user,
// and store it in the bakery.Service's storage. Callers should first ensure
// the user is valid before calling this, to avoid filling storage with keys
// for invalid users.
func (u *UserAuthenticator) CreateLocalLoginMacaroon(tag names.UserTag) (*macaroon.Macaroon, error) {

	expiryTime := u.Clock.Now().Add(localLoginExpiryTime)

	// Ensure that the private key that we generate and store will be
	// removed from storage once the expiry time has elapsed.
	bakeryService, err := u.Service.ExpireStorageAt(expiryTime)
	if err != nil {
		return nil, errors.Trace(err)
	}

	// We create the macaroon with a random ID and random root key, which
	// enables multiple clients to login as the same user and obtain separate
	// macaroons without having them use the same root key.
	m, err := bakeryService.NewMacaroon("", nil, []checkers.Caveat{
		// The macaroon may only be used to log in as the user
		// specified by the tag passed to CreateLocalUserMacaroon.
		checkers.DeclaredCaveat(usernameKey, tag.Canonical()),
	})
	if err != nil {
		return nil, errors.Annotate(err, "cannot create macaroon")
	}
	if err := addMacaroonTimeBeforeCaveat(bakeryService, m, expiryTime); err != nil {
		return nil, errors.Trace(err)
	}
	return m, nil
}
Exemple #2
0
func (s *CharmStoreSuite) SetUpTest(c *gc.C) {
	s.CleanupSuite.SetUpTest(c)

	s.discharger = bakerytest.NewDischarger(nil, func(_ *http.Request, cond string, arg string) ([]checkers.Caveat, error) {
		if s.DischargeUser == "" {
			return nil, fmt.Errorf("discharge denied")
		}
		return []checkers.Caveat{
			checkers.DeclaredCaveat("username", s.DischargeUser),
		}, nil
	})
	db := s.Session.DB("juju-testing")
	params := charmstore.ServerParams{
		AuthUsername:     "******",
		AuthPassword:     "******",
		IdentityLocation: s.discharger.Location(),
		PublicKeyLocator: s.discharger,
	}
	handler, err := charmstore.NewServer(db, nil, "", params, charmstore.V4)
	c.Assert(err, jc.ErrorIsNil)
	s.handler = handler
	s.Srv = httptest.NewServer(handler)
	s.Client = csclient.New(csclient.Params{
		URL:      s.Srv.URL,
		User:     params.AuthUsername,
		Password: params.AuthPassword,
	})

	s.PatchValue(&charmrepo.CacheDir, c.MkDir())
	s.PatchValue(&service.NewCharmStore, func(p charmrepo.NewCharmStoreParams) charmrepo.Interface {
		p.URL = s.Srv.URL
		return charmrepo.NewCharmStore(p)
	})
}
Exemple #3
0
func (s *charmStoreSuite) SetUpTest(c *gc.C) {
	s.JujuConnSuite.SetUpTest(c)

	// Set up the third party discharger.
	s.discharger = bakerytest.NewDischarger(nil, func(req *http.Request, cond string, arg string) ([]checkers.Caveat, error) {
		cookie, err := req.Cookie(clientUserCookie)
		if err != nil {
			return nil, errors.Annotate(err, "discharge denied to non-clients")
		}
		return []checkers.Caveat{
			checkers.DeclaredCaveat("username", cookie.Value),
		}, nil
	})

	// Set up the charm store testing server.
	db := s.Session.DB("juju-testing")
	params := charmstore.ServerParams{
		AuthUsername:     "******",
		AuthPassword:     "******",
		IdentityLocation: s.discharger.Location(),
		PublicKeyLocator: s.discharger,
	}
	handler, err := charmstore.NewServer(db, nil, "", params, charmstore.V4)
	c.Assert(err, jc.ErrorIsNil)
	s.handler = handler
	s.srv = httptest.NewServer(handler)
	s.client = csclient.New(csclient.Params{
		URL:      s.srv.URL,
		User:     params.AuthUsername,
		Password: params.AuthPassword,
	})

	// Initialize the charm cache dir.
	s.PatchValue(&charmrepo.CacheDir, c.MkDir())

	// Point the CLI to the charm store testing server.
	original := newCharmStoreClient
	s.PatchValue(&newCharmStoreClient, func() (*csClient, error) {
		csclient, err := original()
		if err != nil {
			return nil, err
		}
		csclient.params.URL = s.srv.URL
		// Add a cookie so that the discharger can detect whether the
		// HTTP client is the juju environment or the juju client.
		lurl, err := url.Parse(s.discharger.Location())
		if err != nil {
			panic(err)
		}
		csclient.params.HTTPClient.Jar.SetCookies(lurl, []*http.Cookie{{
			Name:  clientUserCookie,
			Value: clientUserName,
		}})
		return csclient, nil
	})

	// Point the Juju API server to the charm store testing server.
	s.PatchValue(&csclient.ServerURL, s.srv.URL)
}
Exemple #4
0
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)
}
Exemple #5
0
func (m *mockapi) Authorize(modelUUID, charmURL, applicationName, plan string, visitWebPage func(*url.URL) error) (*macaroon.Macaroon, error) {
	err := m.NextErr()
	if err != nil {
		return nil, errors.Trace(err)
	}
	m.AddCall("Authorize", modelUUID, charmURL, applicationName)
	macaroon, err := m.service.NewMacaroon(
		"",
		nil,
		[]checkers.Caveat{
			checkers.DeclaredCaveat("environment", modelUUID),
			checkers.DeclaredCaveat("charm", charmURL),
			checkers.DeclaredCaveat("service", applicationName),
			checkers.DeclaredCaveat("plan", plan),
		},
	)
	if err != nil {
		return nil, errors.Trace(err)
	}
	m.macaroon = macaroon
	return m.macaroon, nil
}
Exemple #6
0
func (s *suite) TestMacaroonAuthorization(c *gc.C) {
	ch := charmRepo.CharmDir("wordpress")
	curl := charm.MustParseReference("~charmers/utopic/wordpress-42")
	purl := charm.MustParseReference("utopic/wordpress-42")
	err := s.client.UploadCharmWithRevision(curl, ch, 42)
	c.Assert(err, gc.IsNil)

	err = s.client.Put("/"+curl.Path()+"/meta/perm/read", []string{"bob"})
	c.Assert(err, gc.IsNil)

	// Create a client without basic auth credentials
	client := csclient.New(csclient.Params{
		URL: s.srv.URL,
	})

	var result struct{ IdRevision struct{ Revision int } }
	// TODO 2015-01-23: once supported, rewrite the test using POST requests.
	_, err = client.Meta(purl, &result)
	c.Assert(err, gc.ErrorMatches, `cannot get "/utopic/wordpress-42/meta/any\?include=id-revision": cannot get discharge from ".*": third party refused discharge: cannot discharge: no discharge`)
	c.Assert(httpbakery.IsDischargeError(errgo.Cause(err)), gc.Equals, true)

	s.discharge = func(cond, arg string) ([]checkers.Caveat, error) {
		return []checkers.Caveat{checkers.DeclaredCaveat("username", "bob")}, nil
	}
	_, err = client.Meta(curl, &result)
	c.Assert(err, gc.IsNil)
	c.Assert(result.IdRevision.Revision, gc.Equals, curl.Revision)

	visitURL := "http://0.1.2.3/visitURL"
	s.discharge = func(cond, arg string) ([]checkers.Caveat, error) {
		return nil, &httpbakery.Error{
			Code:    httpbakery.ErrInteractionRequired,
			Message: "interaction required",
			Info: &httpbakery.ErrorInfo{
				VisitURL: visitURL,
				WaitURL:  "http://0.1.2.3/waitURL",
			}}
	}

	client = csclient.New(csclient.Params{
		URL: s.srv.URL,
		VisitWebPage: func(vurl *neturl.URL) error {
			c.Check(vurl.String(), gc.Equals, visitURL)
			return fmt.Errorf("stopping interaction")
		}})

	_, err = client.Meta(purl, &result)
	c.Assert(err, gc.ErrorMatches, `cannot get "/utopic/wordpress-42/meta/any\?include=id-revision": cannot get discharge from ".*": cannot start interactive session: stopping interaction`)
	c.Assert(result.IdRevision.Revision, gc.Equals, curl.Revision)
	c.Assert(httpbakery.IsInteractionError(errgo.Cause(err)), gc.Equals, true)
}
Exemple #7
0
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")
}
Exemple #8
0
func checkNeedDeclared(caveatId, arg string, checker ThirdPartyChecker) ([]checkers.Caveat, error) {
	i := strings.Index(arg, " ")
	if i <= 0 {
		return nil, errgo.Newf("need-declared caveat requires an argument, got %q", arg)
	}
	needDeclared := strings.Split(arg[0:i], ",")
	for _, d := range needDeclared {
		if d == "" {
			return nil, errgo.New("need-declared caveat with empty required attribute")
		}
	}
	if len(needDeclared) == 0 {
		return nil, fmt.Errorf("need-declared caveat with no required attributes")
	}
	caveats, err := checker.CheckThirdPartyCaveat(caveatId, arg[i+1:])
	if err != nil {
		return nil, errgo.Mask(err, errgo.Any)
	}
	declared := make(map[string]bool)
	for _, cav := range caveats {
		if cav.Location != "" {
			continue
		}
		// Note that we ignore the error. We allow the service to
		// generate caveats that we don't understand here.
		cond, arg, _ := checkers.ParseCaveat(cav.Condition)
		if cond != checkers.CondDeclared {
			continue
		}
		parts := strings.SplitN(arg, " ", 2)
		if len(parts) != 2 {
			return nil, errgo.Newf("declared caveat has no value")
		}
		declared[parts[0]] = true
	}
	// Add empty declarations for everything mentioned in need-declared
	// that was not actually declared.
	for _, d := range needDeclared {
		if !declared[d] {
			caveats = append(caveats, checkers.DeclaredCaveat(d, ""))
		}
	}
	return caveats, nil
}
Exemple #9
0
func (s *MacaroonSuite) SetUpTest(c *gc.C) {
	s.discharger = bakerytest.NewDischarger(nil, func(req *http.Request, cond, arg string) ([]checkers.Caveat, error) {
		if cond != "is-authenticated-user" {
			return nil, errors.New("unknown caveat")
		}
		var username string
		if s.DischargerLogin != nil {
			username = s.DischargerLogin()
		}
		if username == "" {
			return nil, errors.New("login denied by discharger")
		}
		return []checkers.Caveat{checkers.DeclaredCaveat("username", username)}, nil
	})
	s.JujuConnSuite.ConfigAttrs = map[string]interface{}{
		config.IdentityURL: s.discharger.Location(),
	}
	s.JujuConnSuite.SetUpTest(c)
}
Exemple #10
0
// CreateLocalLoginMacaroon creates a time-limited macaroon for a local user
// to log into the controller with. The macaroon will be valid for use with
// UserAuthenticator.Authenticate until the time limit expires, or the Juju
// controller agent restarts.
//
// NOTE(axw) this method will generate a key for a previously unseen user,
// and store it in the bakery.Service's storage, which is currently in-memory.
// Callers should first ensure the user is valid before calling this, to avoid
// filling memory with keys for invalid users.
func (u *UserAuthenticator) CreateLocalLoginMacaroon(tag names.UserTag) (*macaroon.Macaroon, error) {
	// We create the macaroon with a random ID and random root key, which
	// enables multiple clients to login as the same user and obtain separate
	// macaroons without having them use the same root key.
	//
	// TODO(axw) check with rogpeppe about this. bakery.Service doesn't
	// currently garbage collect, so this will grow until the controller
	// agent restarts.
	m, err := u.Service.NewMacaroon("", nil, []checkers.Caveat{
		// The macaroon may only be used to log in as the user
		// specified by the tag passed to CreateLocalUserMacaroon.
		checkers.DeclaredCaveat(usernameKey, tag.Canonical()),
	})
	if err != nil {
		return nil, errors.Annotate(err, "cannot create macaroon")
	}
	if err := addMacaroonTimeBeforeCaveat(u.Service, m, localLoginExpiryTime); err != nil {
		return nil, errors.Trace(err)
	}
	return m, nil
}
Exemple #11
0
Fichier : user.go Projet : bac/juju
// CheckLocalLoginRequest checks that the given HTTP request contains at least
// one valid local login macaroon minted by the given service using
// CreateLocalLoginMacaroon. It returns an error with a
// *bakery.VerificationError cause if the macaroon verification failed. If the
// macaroon is valid, CheckLocalLoginRequest returns a list of caveats to add
// to the discharge macaroon.
func CheckLocalLoginRequest(
	service *bakery.Service,
	req *http.Request,
	tag names.UserTag,
	clock clock.Clock,
) ([]checkers.Caveat, error) {
	_, err := httpbakery.CheckRequest(service, req, nil, checkers.CheckerFunc{
		// Having a macaroon with an is-authenticated-user
		// caveat is proof that the user is "logged in".
		"is-authenticated-user",
		func(cond, arg string) error { return nil },
	})
	if err != nil {
		return nil, errors.Trace(err)
	}
	firstPartyCaveats := []checkers.Caveat{
		checkers.DeclaredCaveat("username", tag.Id()),
		checkers.TimeBeforeCaveat(clock.Now().Add(localLoginExpiryTime)),
	}
	return firstPartyCaveats, nil
}
Exemple #12
0
func (s *userAuthenticatorSuite) TestCreateLocalLoginMacaroon(c *gc.C) {
	service := mockBakeryService{}
	clock := coretesting.NewClock(time.Time{})
	authenticator := &authentication.UserAuthenticator{
		Service: &service,
		Clock:   clock,
	}

	_, err := authenticator.CreateLocalLoginMacaroon(names.NewUserTag("bobbrown"))
	c.Assert(err, jc.ErrorIsNil)

	service.CheckCallNames(c, "ExpireStorageAt", "NewMacaroon", "AddCaveat")
	calls := service.Calls()
	c.Assert(calls[0].Args, jc.DeepEquals, []interface{}{clock.Now().Add(24 * time.Hour)})
	c.Assert(calls[1].Args, jc.DeepEquals, []interface{}{
		"", []byte(nil), []checkers.Caveat{
			checkers.DeclaredCaveat("username", "bobbrown@local"),
		},
	})
	c.Assert(calls[2].Args, jc.DeepEquals, []interface{}{
		&macaroon.Macaroon{},
		checkers.TimeBeforeCaveat(clock.Now().Add(24 * time.Hour)),
	})
}
Exemple #13
0
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"`)
}
Exemple #14
0
func (s *charmStoreSuite) SetUpTest(c *gc.C) {
	s.JujuConnSuite.SetUpTest(c)

	// Set up the third party discharger.
	s.discharger = bakerytest.NewDischarger(nil, func(req *http.Request, cond string, arg string) ([]checkers.Caveat, error) {
		cookie, err := req.Cookie(clientUserCookie)
		if err != nil {
			return nil, errors.Annotate(err, "discharge denied to non-clients")
		}
		return []checkers.Caveat{
			checkers.DeclaredCaveat("username", cookie.Value),
		}, nil
	})

	s.termsDischargerError = nil
	// Set up the third party terms discharger.
	s.termsDischarger = bakerytest.NewDischarger(nil, func(req *http.Request, cond string, arg string) ([]checkers.Caveat, error) {
		s.termsString = arg
		return nil, s.termsDischargerError
	})
	s.termsString = ""

	keyring := bakery.NewPublicKeyRing()

	pk, err := httpbakery.PublicKeyForLocation(http.DefaultClient, s.discharger.Location())
	c.Assert(err, gc.IsNil)
	err = keyring.AddPublicKeyForLocation(s.discharger.Location(), true, pk)
	c.Assert(err, gc.IsNil)

	pk, err = httpbakery.PublicKeyForLocation(http.DefaultClient, s.termsDischarger.Location())
	c.Assert(err, gc.IsNil)
	err = keyring.AddPublicKeyForLocation(s.termsDischarger.Location(), true, pk)
	c.Assert(err, gc.IsNil)

	// Set up the charm store testing server.
	db := s.Session.DB("juju-testing")
	params := charmstore.ServerParams{
		AuthUsername:     "******",
		AuthPassword:     "******",
		IdentityLocation: s.discharger.Location(),
		PublicKeyLocator: keyring,
		TermsLocation:    s.termsDischarger.Location(),
	}
	handler, err := charmstore.NewServer(db, nil, "", params, charmstore.V5)
	c.Assert(err, jc.ErrorIsNil)
	s.handler = handler
	s.srv = httptest.NewServer(handler)
	c.Logf("started charmstore on %v", s.srv.URL)
	s.client = csclient.New(csclient.Params{
		URL:      s.srv.URL,
		User:     params.AuthUsername,
		Password: params.AuthPassword,
	})

	// Initialize the charm cache dir.
	s.PatchValue(&charmrepo.CacheDir, c.MkDir())

	// Point the CLI to the charm store testing server.
	s.PatchValue(&newCharmStoreClient, func(client *httpbakery.Client) *csclient.Client {
		// Add a cookie so that the discharger can detect whether the
		// HTTP client is the juju environment or the juju client.
		lurl, err := url.Parse(s.discharger.Location())
		c.Assert(err, jc.ErrorIsNil)
		client.Jar.SetCookies(lurl, []*http.Cookie{{
			Name:  clientUserCookie,
			Value: clientUserName,
		}})
		return csclient.New(csclient.Params{
			URL:          s.srv.URL,
			BakeryClient: client,
		})
	})

	// Point the Juju API server to the charm store testing server.
	s.PatchValue(&csclient.ServerURL, s.srv.URL)
}
		expectError: `caveat "time-before 2006-01-02T15:04:05.123Z" not satisfied: macaroon has expired`,
	}, {
		caveat:      checkers.TimeBeforeCaveat(now.Add(-1)).Condition,
		expectError: `caveat "time-before 2006-01-02T15:04:05.122999999Z" not satisfied: macaroon has expired`,
	}, {
		caveat:      `time-before bad-date`,
		expectError: `caveat "time-before bad-date" not satisfied: parsing time "bad-date" as "2006-01-02T15:04:05.999999999Z07:00": cannot parse "bad-date" as "2006"`,
	}, {
		caveat:      checkers.TimeBeforeCaveat(now).Condition + " ",
		expectError: `caveat "time-before 2006-01-02T15:04:05.123Z " not satisfied: parsing time "2006-01-02T15:04:05.123Z ": extra text:  `,
	}},
}, {
	about:   "declared, no entries",
	checker: checkers.New(checkers.Declared{}),
	checks: []checkTest{{
		caveat:      checkers.DeclaredCaveat("a", "aval").Condition,
		expectError: `caveat "declared a aval" not satisfied: got a=null, expected "aval"`,
	}, {
		caveat:      checkers.CondDeclared,
		expectError: `caveat "declared" not satisfied: declared caveat has no value`,
	}},
}, {
	about: "declared, some entries",
	checker: checkers.New(checkers.Declared{
		"a":   "aval",
		"b":   "bval",
		"spc": " a b",
	}),
	checks: []checkTest{{
		caveat: checkers.DeclaredCaveat("a", "aval").Condition,
	}, {
Exemple #16
0
func (s *macaroonAuthenticatorSuite) Checker(req *http.Request, cond, arg string) ([]checkers.Caveat, error) {
	return []checkers.Caveat{checkers.DeclaredCaveat("username", s.username)}, nil
}
Exemple #17
0
func (s *ServiceSuite) TestNeedDeclared(c *gc.C) {
	locator := make(bakery.PublicKeyLocatorMap)
	firstParty := newService(c, "first", locator)
	thirdParty := newService(c, "third", locator)

	// firstParty mints a macaroon with a third-party caveat addressed
	// to thirdParty with a need-declared caveat.
	m, err := firstParty.NewMacaroon("", nil, []checkers.Caveat{
		checkers.NeedDeclaredCaveat(checkers.Caveat{
			Location:  "third",
			Condition: "something",
		}, "foo", "bar"),
	})
	c.Assert(err, gc.IsNil)

	// The client asks for a discharge macaroon for each third party caveat.
	d, err := bakery.DischargeAll(m, func(_ string, cav macaroon.Caveat) (*macaroon.Macaroon, error) {
		return thirdParty.Discharge(strcmpChecker("something"), cav.Id)
	})
	c.Assert(err, gc.IsNil)

	// The required declared attributes should have been added
	// to the discharge macaroons.
	declared := checkers.InferDeclared(d)
	c.Assert(declared, gc.DeepEquals, checkers.Declared{
		"foo": "",
		"bar": "",
	})

	// Make sure the macaroons actually check out correctly
	// when provided with the declared checker.
	err = firstParty.Check(d, checkers.New(declared))
	c.Assert(err, gc.IsNil)

	// Try again when the third party does add a required declaration.

	// The client asks for a discharge macaroon for each third party caveat.
	d, err = bakery.DischargeAll(m, func(_ string, cav macaroon.Caveat) (*macaroon.Macaroon, error) {
		checker := thirdPartyCheckerWithCaveats{
			checkers.DeclaredCaveat("foo", "a"),
			checkers.DeclaredCaveat("arble", "b"),
		}
		return thirdParty.Discharge(checker, cav.Id)
	})
	c.Assert(err, gc.IsNil)

	// One attribute should have been added, the other was already there.
	declared = checkers.InferDeclared(d)
	c.Assert(declared, gc.DeepEquals, checkers.Declared{
		"foo":   "a",
		"bar":   "",
		"arble": "b",
	})

	err = firstParty.Check(d, checkers.New(declared))
	c.Assert(err, gc.IsNil)

	// Try again, but this time pretend a client is sneakily trying
	// to add another "declared" attribute to alter the declarations.
	d, err = bakery.DischargeAll(m, func(_ string, cav macaroon.Caveat) (*macaroon.Macaroon, error) {
		checker := thirdPartyCheckerWithCaveats{
			checkers.DeclaredCaveat("foo", "a"),
			checkers.DeclaredCaveat("arble", "b"),
		}
		m, err := thirdParty.Discharge(checker, cav.Id)
		c.Assert(err, gc.IsNil)

		// Sneaky client adds a first party caveat.
		m.AddFirstPartyCaveat(checkers.DeclaredCaveat("foo", "c").Condition)
		return m, nil
	})
	c.Assert(err, gc.IsNil)

	declared = checkers.InferDeclared(d)
	c.Assert(declared, gc.DeepEquals, checkers.Declared{
		"bar":   "",
		"arble": "b",
	})

	err = firstParty.Check(d, checkers.New(declared))
	c.Assert(err, gc.ErrorMatches, `verification failed: caveat "declared foo a" not satisfied: got foo=null, expected "a"`)
}
Exemple #18
0
func (*ServiceSuite) TestCheckAnyWithNoMacaroons(c *gc.C) {
	svc := newService(c, "somewhere", nil)
	newMacaroons := func(caveats ...checkers.Caveat) macaroon.Slice {
		m, err := svc.NewMacaroon("", nil, caveats)
		c.Assert(err, gc.IsNil)
		return macaroon.Slice{m}
	}
	tests := []struct {
		about          string
		macaroons      []macaroon.Slice
		assert         map[string]string
		checker        checkers.Checker
		expectDeclared map[string]string
		expectError    string
	}{{
		about:       "no macaroons",
		expectError: "verification failed: no macaroons",
	}, {
		about: "one macaroon, no caveats",
		macaroons: []macaroon.Slice{
			newMacaroons(),
		},
	}, {
		about: "one macaroon, one unrecognized caveat",
		macaroons: []macaroon.Slice{
			newMacaroons(checkers.Caveat{
				Condition: "bad",
			}),
		},
		expectError: `verification failed: caveat "bad" not satisfied: caveat not recognized`,
	}, {
		about: "two macaroons, only one ok",
		macaroons: []macaroon.Slice{
			newMacaroons(checkers.Caveat{
				Condition: "bad",
			}),
			newMacaroons(),
		},
	}, {
		about: "macaroon with declared caveats",
		macaroons: []macaroon.Slice{
			newMacaroons(
				checkers.DeclaredCaveat("key1", "value1"),
				checkers.DeclaredCaveat("key2", "value2"),
			),
		},
		expectDeclared: map[string]string{
			"key1": "value1",
			"key2": "value2",
		},
	}, {
		about: "macaroon with declared values and asserted keys with wrong value",
		macaroons: []macaroon.Slice{
			newMacaroons(
				checkers.DeclaredCaveat("key1", "value1"),
				checkers.DeclaredCaveat("key2", "value2"),
			),
		},
		assert: map[string]string{
			"key1": "valuex",
		},
		expectError: `verification failed: caveat "declared key1 value1" not satisfied: got key1="valuex", expected "value1"`,
	}, {
		about: "macaroon with declared values and asserted keys with correct value",
		macaroons: []macaroon.Slice{
			newMacaroons(
				checkers.DeclaredCaveat("key1", "value1"),
				checkers.DeclaredCaveat("key2", "value2"),
			),
		},
		assert: map[string]string{
			"key1": "value1",
		},
		expectDeclared: map[string]string{
			"key1": "value1",
			"key2": "value2",
		},
	}}
	for i, test := range tests {
		c.Logf("test %d: %s", i, test.about)
		if test.expectDeclared == nil {
			test.expectDeclared = make(map[string]string)
		}
		if test.checker == nil {
			test.checker = checkers.New()
		}

		decl, err := svc.CheckAny(test.macaroons, test.assert, test.checker)
		if test.expectError != "" {
			c.Assert(err, gc.ErrorMatches, test.expectError)
			c.Assert(decl, gc.HasLen, 0)
			continue
		}
		c.Assert(err, gc.IsNil)
		c.Assert(decl, jc.DeepEquals, test.expectDeclared)
	}
}