Example #1
0
File: service.go Project: cmars/oo
// Discharge creates a macaroon that discharges the third party caveat with the
// given id that should have been created earlier using key.Public. The
// condition implicit in the id is checked for validity using checker. If
// it is valid, a new macaroon is returned which discharges the caveat
// along with any caveats returned from the checker.
func Discharge(key *KeyPair, checker ThirdPartyChecker, id string) (*macaroon.Macaroon, []checkers.Caveat, error) {
	decoder := newBoxDecoder(key)

	logger.Infof("server attempting to discharge %q", id)
	rootKey, condition, err := decoder.decodeCaveatId(id)
	if err != nil {
		return nil, nil, errgo.Notef(err, "discharger cannot decode caveat id")
	}
	// Note that we don't check the error - we allow the
	// third party checker to see even caveats that we can't
	// understand.
	cond, arg, _ := checkers.ParseCaveat(condition)
	var caveats []checkers.Caveat
	if cond == checkers.CondNeedDeclared {
		caveats, err = checkNeedDeclared(id, arg, checker)
	} else {
		caveats, err = checker.CheckThirdPartyCaveat(id, condition)
	}
	if err != nil {
		return nil, nil, errgo.Mask(err, errgo.Any)
	}
	// Note that the discharge macaroon does not need to
	// be stored persistently. Indeed, it would be a problem if
	// we did, because then the macaroon could potentially be used
	// for normal authorization with the third party.
	m, err := macaroon.New(rootKey, id, "")
	if err != nil {
		return nil, nil, errgo.Mask(err)
	}
	return m, caveats, nil
}
Example #2
0
// NewDischarger returns a new third party caveat discharger
// which uses the given function to check caveats.
// The cond and arg arguments to the function are as returned
// by checkers.ParseCaveat.
//
// If locator is non-nil, it will be used to find public keys
// for any third party caveats returned by the checker.
//
// Calling this function has the side-effect of setting
// InsecureSkipVerify in http.DefaultTransport.TLSClientConfig
// until all the dischargers are closed.
func NewDischarger(
	locator bakery.PublicKeyLocator,
	checker func(req *http.Request, cond, arg string) ([]checkers.Caveat, error),
) *Discharger {
	mux := http.NewServeMux()
	server := httptest.NewTLSServer(mux)
	svc, err := bakery.NewService(bakery.NewServiceParams{
		Location: server.URL,
		Locator:  locator,
	})
	if err != nil {
		panic(err)
	}
	checker1 := func(req *http.Request, cavId, cav string) ([]checkers.Caveat, error) {
		cond, arg, err := checkers.ParseCaveat(cav)
		if err != nil {
			return nil, err
		}
		return checker(req, cond, arg)
	}
	httpbakery.AddDischargeHandler(mux, "/", svc, checker1)
	startSkipVerify()
	return &Discharger{
		Service: svc,
		server:  server,
	}
}
func (*CheckersSuite) TestOperationsChecker(c *gc.C) {
	for i, test := range operationsCheckerTests {
		c.Logf("%d: %s", i, test.about)
		cond, arg, err := checkers.ParseCaveat(test.caveat.Condition)
		c.Assert(err, gc.IsNil)
		c.Assert(test.oc.Condition(), gc.Equals, "")
		err = test.oc.Check(cond, arg)
		if test.expectError == "" {
			c.Assert(err, gc.IsNil)
			continue
		}
		c.Assert(err, gc.ErrorMatches, test.expectError)
	}
}
Example #4
0
File: service.go Project: cmars/oo
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
}
Example #5
0
File: user.go Project: bac/juju
// CheckLocalLoginCaveat parses and checks that the given caveat string is
// valid for a local login request, and returns the tag of the local user
// that the caveat asserts is logged in. checkers.ErrCaveatNotRecognized will
// be returned if the caveat is not recognised.
func CheckLocalLoginCaveat(caveat string) (names.UserTag, error) {
	var tag names.UserTag
	op, rest, err := checkers.ParseCaveat(caveat)
	if err != nil {
		return tag, errors.Annotatef(err, "cannot parse caveat %q", caveat)
	}
	if op != "is-authenticated-user" {
		return tag, checkers.ErrCaveatNotRecognized
	}
	if !names.IsValidUser(rest) {
		return tag, errors.NotValidf("username %q", rest)
	}
	tag = names.NewUserTag(rest)
	if !tag.IsLocal() {
		tag = names.UserTag{}
		return tag, errors.NotValidf("non-local username %q", rest)
	}
	return tag, nil
}
Example #6
0
func (ctxt *context) CheckThirdPartyCaveat(cavId, cav string) ([]checkers.Caveat, error) {
	h := ctxt.handler
	log.Printf("checking third party caveat %q", cav)
	op, rest, err := checkers.ParseCaveat(cav)
	if err != nil {
		return nil, fmt.Errorf("cannot parse caveat %q: %v", cav, err)
	}
	switch op {
	case "can-speak-for":
		// TODO(rog) We ignore the currently logged in user here,
		// but perhaps it would be better to let the user be in control
		// of which user they're currently "declared" as, rather than
		// getting privileges of users we currently have macaroons for.
		checkErr := ctxt.canSpeakFor(rest)
		if checkErr == nil {
			return ctxt.firstPartyCaveats(), nil
		}
		return nil, h.needLogin(cavId, cav, checkErr, ctxt.req)
	case "member-of-group":
		// The third-party caveat is asking if the currently logged in
		// user is a member of a particular group.
		// We can find the currently logged in user by checking
		// the username cookie (which doesn't provide any power, but
		// indicates which user name to check)
		if ctxt.declaredUser == "" {
			return nil, h.needLogin(cavId, cav, errgo.New("not logged in"), ctxt.req)
		}
		if err := ctxt.canSpeakFor(ctxt.declaredUser); err != nil {
			return nil, errgo.Notef(err, "cannot speak for declared user %q", ctxt.declaredUser)
		}
		info, ok := h.users[ctxt.declaredUser]
		if !ok {
			return nil, errgo.Newf("user %q not found", ctxt.declaredUser)
		}
		group := rest
		if !info.Groups[group] {
			return nil, errgo.Newf("not privileged enough")
		}
		return ctxt.firstPartyCaveats(), nil
	default:
		return nil, checkers.ErrCaveatNotRecognized
	}
}
Example #7
0
func objectID(ms macaroon.Slice) (string, error) {
	var fail string
	var id string
	for _, m := range ms {
		for _, cav := range m.Caveats() {
			cond, arg, err := checkers.ParseCaveat(cav.Id)
			if err != nil {
				// strange, but offtopic
				continue
			}
			if cond == "object" {
				if id == "" {
					id = arg
				} else {
					return fail, fmt.Errorf("multiple conflicting caveats")
				}
			}
		}
	}
	if id == "" {
		return fail, errors.New("not found")
	}
	return id, nil
}