// checkers implements the caveat checking for the service. // Note how we add context-sensitive checkers // (remote-host checks information from the HTTP request) // to the standard checkers implemented by checkers.Std. func (svc *targetServiceHandler) checkers(req *http.Request, operation string) bakery.FirstPartyChecker { m := checkers.Map{ "remote-host": func(s string) error { // TODO(rog) do we want to distinguish between // the two kinds of errors below? _, host, err := checkers.ParseCaveat(s) if err != nil { return err } remoteHost, _, err := net.SplitHostPort(req.RemoteAddr) if err != nil { return fmt.Errorf("cannot parse request remote address") } if remoteHost != host { return fmt.Errorf("remote address mismatch (need %q, got %q)", host, remoteHost) } return nil }, "operation": func(s string) error { _, op, err := checkers.ParseCaveat(s) if err != nil { return err } if op != operation { return fmt.Errorf("macaroon not valid for operation") } return nil }, } return checkers.PushFirstPartyChecker(m, checkers.Std) }
func (ctxt *context) CheckThirdPartyCaveat(cavId, cav string) ([]bakery.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.Error()) 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, "not logged in") } 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, &bakery.CaveatNotRecognizedError{cav} } }
func (ctxt *context) CheckFirstPartyCaveat(caveat string) error { op, rest, err := checkers.ParseCaveat(caveat) if err != nil { return fmt.Errorf("cannot parse caveat %q: %v", caveat, err) } switch op { case "user-is": if rest != ctxt.declaredUser { return fmt.Errorf("not logged in as %q", rest) } return nil case "operation": if ctxt.operation != "" && rest == ctxt.operation { return nil } return errgo.Newf("operation mismatch") default: return &bakery.CaveatNotRecognizedError{caveat} } }