Example #1
0
// writeError writes an error to w in response to req. If the error was
// generated because of a required macaroon that the client does not
// have, we mint a macaroon that, when discharged, will grant the client
// the right to execute the given operation.
//
// The logic in this function is crucial to the security of the service
// - it must determine for a given operation what caveats to attach.
func (srv *targetServiceHandler) writeError(w http.ResponseWriter, req *http.Request, operation string, verr error) {
	log.Printf("writing error with operation %q", operation)
	fail := func(code int, msg string, args ...interface{}) {
		if code == http.StatusInternalServerError {
			msg = "internal error: " + msg
		}
		http.Error(w, fmt.Sprintf(msg, args...), code)
	}

	if _, ok := errgo.Cause(verr).(*bakery.VerificationError); !ok {
		fail(http.StatusForbidden, "%v", verr)
		return
	}

	// Work out what caveats we need to apply for the given operation.
	// Could special-case the operation here if desired.
	caveats := []checkers.Caveat{
		checkers.TimeBeforeCaveat(time.Now().Add(5 * time.Minute)),
		{
			Location:  srv.authEndpoint,
			Condition: "access-allowed",
		}, {
			Condition: "operation " + operation,
		}}
	// Mint an appropriate macaroon and send it back to the client.
	m, err := srv.svc.NewMacaroon("", nil, caveats)
	if err != nil {
		fail(http.StatusInternalServerError, "cannot mint macaroon: %v", err)
		return
	}
	httpbakery.WriteDischargeRequiredErrorForRequest(w, m, "", verr, req)
}
Example #2
0
func (s *ClientSuite) TestNewCookieExpires(c *gc.C) {
	t := time.Now().Add(24 * time.Hour)
	svc := newService("loc", nil)
	m, err := svc.NewMacaroon("", nil, []checkers.Caveat{
		checkers.TimeBeforeCaveat(t),
	})
	c.Assert(err, gc.IsNil)
	cookie, err := httpbakery.NewCookie(macaroon.Slice{m})
	c.Assert(err, gc.IsNil)
	c.Assert(cookie.Expires.Equal(t), gc.Equals, true, gc.Commentf("obtained: %s, expected: %s", cookie.Expires, t))
}
Example #3
0
File: user.go Project: bac/juju
// CreateLocalLoginMacaroon creates a macaroon that may be provided to a
// user as proof that they have logged in with a valid username and password.
// This macaroon may then be used to obtain a discharge macaroon so that
// the user can log in without presenting their password for a set amount
// of time.
func CreateLocalLoginMacaroon(
	tag names.UserTag,
	service BakeryService,
	clock clock.Clock,
) (*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.
	return service.NewMacaroon("", nil, []checkers.Caveat{
		{Condition: "is-authenticated-user " + tag.Id()},
		checkers.TimeBeforeCaveat(clock.Now().Add(LocalLoginInteractionTimeout)),
	})
}
Example #4
0
File: user.go Project: bac/juju
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
}
Example #5
0
File: user.go Project: 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
}
Example #6
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)),
	})
}
Example #7
0
File: user.go Project: imoapps/juju
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,
	}
}
Example #8
0
File: user.go Project: bac/juju
func addMacaroonTimeBeforeCaveat(svc BakeryService, m *macaroon.Macaroon, t time.Time) error {
	return svc.AddCaveat(m, checkers.TimeBeforeCaveat(t))
}
	}, {
		caveat:      "a wrong",
		expectError: `caveat "a wrong" not satisfied: wrong arg`,
		expectCause: errgo.Is(errWrongArg),
	}, {
		caveat:      "b wrong",
		expectError: `caveat "b wrong" not satisfied: wrong arg`,
		expectCause: errgo.Is(errWrongArg),
	}},
}, {
	about: "time within limit",
	checker: checkers.New(
		checkers.TimeBefore,
	),
	checks: []checkTest{{
		caveat: checkers.TimeBeforeCaveat(now.Add(1)).Condition,
	}, {
		caveat:      checkers.TimeBeforeCaveat(now).Condition,
		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",
Example #10
0
func addMacaroonTimeBeforeCaveat(svc *bakery.Service, m *macaroon.Macaroon, d time.Duration) error {
	return svc.AddCaveat(m, checkers.TimeBeforeCaveat(time.Now().Add(d)))
}
Example #11
0
var expireTimeTests = []struct {
	about         string
	caveats       []macaroon.Caveat
	expectTime    time.Time
	expectExpires bool
}{{
	about: "nil caveats",
}, {
	about:   "empty caveats",
	caveats: []macaroon.Caveat{},
}, {
	about: "single time-before caveat",
	caveats: []macaroon.Caveat{
		macaroon.Caveat{
			Id: checkers.TimeBeforeCaveat(t1).Condition,
		},
	},
	expectTime:    t1,
	expectExpires: true,
}, {
	about: "single deny caveat",
	caveats: []macaroon.Caveat{
		macaroon.Caveat{
			Id: checkers.DenyCaveat("abc").Condition,
		},
	},
}, {
	about: "multiple time-before caveat",
	caveats: []macaroon.Caveat{
		macaroon.Caveat{