func (m *ExternalMacaroonAuthenticator) newDischargeRequiredError(cause error) error { if m.Service == nil || m.Macaroon == nil { return errors.Trace(cause) } mac := m.Macaroon.Clone() // TODO(fwereade): 2016-03-17 lp:1558657 expiryTime := time.Now().Add(externalLoginExpiryTime) if err := addMacaroonTimeBeforeCaveat(m.Service, mac, expiryTime); err != nil { return errors.Annotatef(err, "cannot create macaroon") } err := m.Service.AddCaveat(mac, checkers.NeedDeclaredCaveat( checkers.Caveat{ Location: m.IdentityLocation, Condition: "is-authenticated-user", }, usernameKey, )) if err != nil { return errors.Annotatef(err, "cannot create macaroon") } return &common.DischargeRequiredError{ Cause: cause, Macaroon: mac, } }
func (s *userAuthenticatorSuite) TestAuthenticateLocalLoginMacaroon(c *gc.C) { service := mockBakeryService{} clock := testing.NewClock(time.Time{}) authenticator := &authentication.UserAuthenticator{ Service: &service, Clock: clock, LocalUserIdentityLocation: "https://testing.invalid:1234/auth", } service.SetErrors(&bakery.VerificationError{}) _, err := authenticator.Authenticate( authentication.EntityFinder(nil), names.NewUserTag("bobbrown"), params.LoginRequest{}, ) c.Assert(err, gc.FitsTypeOf, &common.DischargeRequiredError{}) service.CheckCallNames(c, "CheckAny", "ExpireStorageAt", "NewMacaroon") calls := service.Calls() c.Assert(calls[1].Args, jc.DeepEquals, []interface{}{clock.Now().Add(24 * time.Hour)}) c.Assert(calls[2].Args, jc.DeepEquals, []interface{}{ "", []byte(nil), []checkers.Caveat{ checkers.NeedDeclaredCaveat( checkers.Caveat{ Location: "https://testing.invalid:1234/auth", Condition: "is-authenticated-user bobbrown@local", }, "username", ), {Condition: "time-before 0001-01-02T00:00:00Z"}, }, }) }
func (u *UserAuthenticator) authenticateMacaroons( entityFinder EntityFinder, tag names.UserTag, req params.LoginRequest, ) (state.Entity, error) { // Check for a valid request macaroon. assert := map[string]string{usernameKey: tag.Id()} _, err := u.Service.CheckAny(req.Macaroons, assert, checkers.New(checkers.TimeBefore)) if err != nil { cause := err logger.Debugf("local-login macaroon authentication failed: %v", cause) if _, ok := errors.Cause(err).(*bakery.VerificationError); !ok { return nil, errors.Trace(err) } // The root keys for these macaroons are stored in MongoDB. // Expire the documents after after a set amount of time. expiryTime := u.Clock.Now().Add(localLoginExpiryTime) service, err := u.Service.ExpireStorageAt(expiryTime) if err != nil { return nil, errors.Trace(err) } m, err := service.NewMacaroon("", nil, []checkers.Caveat{ checkers.NeedDeclaredCaveat( checkers.Caveat{ Location: u.LocalUserIdentityLocation, Condition: "is-authenticated-user " + tag.Id(), }, usernameKey, ), checkers.TimeBeforeCaveat(expiryTime), }) if err != nil { return nil, errors.Annotate(err, "cannot create macaroon") } return nil, &common.DischargeRequiredError{ Cause: cause, Macaroon: m, } } entity, err := entityFinder.FindEntity(tag) if errors.IsNotFound(err) { logger.Debugf("entity %s not found", tag.String()) return nil, errors.Trace(common.ErrBadCreds) } else if err != nil { return nil, errors.Trace(err) } return entity, nil }
func (m *MacaroonAuthenticator) newDischargeRequiredError(cause error) error { if m.Service == nil || m.Macaroon == nil { return errors.Trace(cause) } mac := m.Macaroon.Clone() err := m.Service.AddCaveat(mac, checkers.TimeBeforeCaveat(time.Now().Add(time.Hour))) if err != nil { return errors.Annotatef(err, "cannot create macaroon") } err = m.Service.AddCaveat(mac, checkers.NeedDeclaredCaveat( checkers.Caveat{ Location: m.IdentityLocation, Condition: "is-authenticated-user", }, usernameKey, )) if err != nil { return errors.Annotatef(err, "cannot create macaroon") } return &common.DischargeRequiredError{ Cause: cause, Macaroon: mac, } }
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"`) }