func (s *Service) checkRequest(info requestInfo) (*authInfo, error) { var ms macaroon.Slice err := json.NewDecoder(info.request.Body).Decode(&ms) if err != nil { return nil, errgo.Mask(err, errgo.Any) } declared := checkers.InferDeclared(ms) // TODO: assert any declared caveats here err = s.bakery.Check(ms, checkers.New(declared, newCheckers(info))) if err != nil { return nil, errgo.Mask(err, errgo.Any) } return &authInfo{ object: info.params.ByName("object"), declared: declared, }, nil }
func (*CheckersSuite) TestInferDeclared(c *gc.C) { for i, test := range inferDeclaredTests { c.Logf("test %d: %s", i, test.about) ms := make(macaroon.Slice, len(test.caveats)) for i, caveats := range test.caveats { m, err := macaroon.New(nil, fmt.Sprint(i), "") c.Assert(err, gc.IsNil) for _, cav := range caveats { if cav.Location == "" { m.AddFirstPartyCaveat(cav.Condition) } else { m.AddThirdPartyCaveat(nil, cav.Condition, cav.Location) } } ms[i] = m } c.Assert(checkers.InferDeclared(ms), jc.DeepEquals, test.expect) } }
// CheckAny checks that the given slice of slices contains at least // one macaroon minted by the given service, using checker to check // any first party caveats. It returns an error with a // *bakery.VerificationError cause if the macaroon verification failed. // // The assert map holds any required attributes of "declared" attributes, // overriding any inferences made from the macaroons themselves. // It has a similar effect to adding a checkers.DeclaredCaveat // for each key and value, but the error message will be more // useful. // // It adds all the standard caveat checkers to the given checker. // // It returns any attributes declared in the successfully validated request. func (svc *Service) CheckAny(mss []macaroon.Slice, assert map[string]string, checker checkers.Checker) (map[string]string, error) { if len(mss) == 0 { return nil, &VerificationError{ Reason: errgo.Newf("no macaroons"), } } // TODO perhaps return a slice of attribute maps, one // for each successfully validated macaroon slice? var err error for _, ms := range mss { declared := checkers.InferDeclared(ms) for key, val := range assert { declared[key] = val } err = svc.Check(ms, checkers.New(declared, checker)) if err == nil { return declared, nil } } // Return an arbitrary error from the macaroons provided. // TODO return all errors. return nil, errgo.Mask(err, isVerificationError) }
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"`) }
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"`) }