Esempio n. 1
0
// AllowCapability checks that the user is allowed to perform all the
// given operations. If not, the error will be as returned from Allow.
//
// If AllowCapability succeeds, it returns a list of first party caveat
// conditions that must be applied to any macaroon granting capability
// to execute the operations.
//
// If ops contains LoginOp, the user must have been authenticated with a
// macaroon associated with the single operation LoginOp only.
func (a *Authorizer) AllowCapability(ctxt context.Context, ops []Op) ([]string, error) {
	nops := 0
	for _, op := range ops {
		if op != LoginOp {
			nops++
		}
	}
	if nops == 0 {
		return nil, errgo.Newf("no non-login operations required in capability")
	}
	_, used, err := a.allowAny(ctxt, ops)
	if err != nil {
		logger.Infof("allowAny returned used %v; err %v", used, err)
		return nil, errgo.Mask(err, isDischargeRequiredError)
	}
	var squasher caveatSquasher
	for i, isUsed := range used {
		if !isUsed {
			continue
		}
		for _, cond := range a.conditions[i] {
			squasher.add(cond)
		}
	}
	return squasher.final(), nil
}
Esempio n. 2
0
func (a *Authorizer) checkConditions(ctxt context.Context, op Op, conds []string) (map[string]string, error) {
	logger.Infof("checking conditions %q", conds)
	declared := checkers.InferDeclaredFromConditions(conds)
	ctxt = checkers.ContextWithOperations(ctxt, op.Action)
	ctxt = checkers.ContextWithDeclared(ctxt, declared)
	for _, cond := range conds {
		if err := a.service.caveatChecker.CheckFirstPartyCaveat(ctxt, cond); err != nil {
			return nil, errgo.Mask(err)
		}
	}
	return declared, nil
}
Esempio n. 3
0
func (s *macaroonStore) NewMacaroon(ops []auth.Op, caveats []checkers.Caveat) (*macaroon.Macaroon, error) {
	rootKey, id, err := s.store.RootKey()
	if err != nil {
		return nil, errgo.Mask(err)
	}

	mid := macaroonId{
		Id:  id,
		Ops: ops,
	}
	data, _ := json.Marshal(mid)
	m, err := macaroon.New(rootKey, data, "", macaroon.LatestVersion)
	if err != nil {
		return nil, errgo.Mask(err)
	}
	for _, cav := range caveats {
		if err := bakery.AddCaveat(s.key, s.locator, m, cav); err != nil {
			return nil, errgo.Notef(err, "cannot add caveat")
		}
	}
	return m, nil
}
Esempio n. 4
0
// verifyIgnoringCaveats verifies the given macaroon and its discharges without
// checking any caveats. It returns all the caveats that should
// have been checked.
func verifyIgnoringCaveats(ms macaroon.Slice, rootKey []byte) ([]string, error) {
	var caveats []string
	if len(ms) == 0 {
		return nil, errgo.New("no macaroons in slice")
	}
	if err := ms[0].Verify(rootKey, func(caveat string) error {
		caveats = append(caveats, caveat)
		return nil
	}, ms[1:]); err != nil {
		return nil, errgo.Mask(err)
	}
	return caveats, nil
}
Esempio n. 5
0
func (a *aclUserChecker) Allow(ctxt context.Context, id auth.Identity, ops []auth.Op) (allowed []bool, caveats []checkers.Caveat, err error) {
	defer func() {
		logger.Infof("aclUserChecker.Allow(id %#v, ops %#v -> %v, %v, %v", id, ops, allowed, caveats, err)
	}()
	u, ok := id.(idmclient.ACLUser)
	if id != nil && !ok {
		logger.Infof("warning: user %T is not ACLUser", id)
	}
	allowed = make([]bool, len(ops))
	var allCaveats []checkers.Caveat
	for i, op := range ops {
		acl, caveats, err := a.aclGetter.GetACL(ctxt, op)
		if err != nil {
			return nil, nil, errgo.Mask(err)
		}
		allCaveats = append(allCaveats, caveats...)
		ok, err := allowUser(u, acl)
		allowed[i] = ok
	}
	return allowed, allCaveats, nil
}
Esempio n. 6
0
// allowAny is the internal version of AllowAny. Instead of returning an
// authInfo struct, it returns a slice describing which operations have
// been successfully authorized and a slice describing which macaroons
// have been used in the authorization.
func (a *Authorizer) allowAny(ctxt context.Context, ops []Op) (authed, used []bool, err error) {
	if err := a.init(ctxt); err != nil {
		return nil, nil, errgo.Mask(err)
	}
	logger.Infof("after authorizer init, identity %#v", a.identity)
	used = make([]bool, len(a.macaroons))
	authed = make([]bool, len(ops))
	numAuthed := 0
	for i, op := range ops {
		if op == LoginOp && len(ops) > 1 {
			// LoginOp cannot be combined with other operations in the
			// same macaroon, so ignore it if it is.
			continue
		}
		for _, mindex := range a.authIndexes[op] {
			_, err := a.checkConditions(ctxt, op, a.conditions[mindex])
			if err != nil {
				logger.Infof("caveat check failed: %v", err)
				// log error?
				continue
			}
			authed[i] = true
			numAuthed++
			used[mindex] = true
			break
		}
	}
	if a.identity != nil {
		// We've authenticated as a user, so even if the operations didn't
		// specifically require it, we add the authn macaroon and its
		// conditions to the macaroons used and its con
		indexes := a.authIndexes[LoginOp]
		if len(indexes) == 0 {
			// Should never happen because init ensures it's there.
			panic("no macaroon info found for login op")
		}
		// Note: because we never issue a macaroon which combines LoginOp
		// with other operations, if the login op macaroon is used, we
		// know that it's already checked out successfully with LoginOp,
		// so no need to check again.
		used[indexes[0]] = true
	}
	if numAuthed == len(ops) {
		// All operations allowed.
		return nil, used, nil
	}
	// There are some unauthorized operations.
	need := make([]Op, 0, len(ops)-numAuthed)
	needIndex := make([]int, cap(need))
	for i, ok := range authed {
		if !ok {
			needIndex[len(need)] = i
			need = append(need, ops[i])
		}
	}
	logger.Infof("operations needed after authz macaroons: %#v", need)
	// Try to authorize the operations even even if we haven't got an authenticated user.
	oks, caveats, err := a.service.p.UserChecker.Allow(ctxt, a.identity, need)
	if err != nil {
		return authed, used, errgo.Notef(err, "cannot check permissions")
	}
	if len(oks) != len(need) {
		return authed, used, errgo.Newf("unexpected slice length returned from Allow (got %d; want %d)", len(oks), len(need))
	}

	stillNeed := make([]Op, 0, len(need))
	for i, ok := range oks {
		if ok {
			authed[needIndex[i]] = true
		} else {
			stillNeed = append(stillNeed, ops[needIndex[i]])
		}
	}
	if len(stillNeed) == 0 && len(caveats) == 0 {
		// No more ops need to be authenticated and no caveats to be discharged.
		return authed, used, nil
	}
	logger.Infof("operations still needed after auth check: %#v", stillNeed)
	if a.identity == nil {
		// User hasn't authenticated - ask them to do so.
		return authed, used, &DischargeRequiredError{
			Message: "authentication required",
			Ops:     []Op{LoginOp},
			Caveats: a.service.p.IdentityClient.IdentityCaveats(),
		}
	}
	if len(caveats) == 0 {
		return authed, used, ErrPermissionDenied
	}
	return authed, used, &DischargeRequiredError{
		Message: "some operations have extra caveats",
		Ops:     ops,
		Caveats: caveats,
	}
}