// Create runs the create action. func (c *UserspaceController) Create(ctx *app.CreateUserspaceContext) error { return models.Transactional(c.db, func(db *gorm.DB) error { path := ctx.RequestURI data := Data{} err := c.db.Where("path = ?", path).First(&data).Error fmt.Println(err) if err != nil { data = Data{ ID: uuid.NewV4(), Path: ctx.RequestURI, Data: workitem.Fields(ctx.Payload), } err := c.db.Create(&data).Error if err != nil { goa.LogError(ctx, "error adding data", "error", err.Error()) return ctx.InternalServerError() } } else { err := c.db.Model(&data).Update("data", workitem.Fields(ctx.Payload)).Error if err != nil { goa.LogError(ctx, "error updating data", "error", err.Error()) return ctx.InternalServerError() } } return ctx.NoContent() }) }
// Run makes the HTTP request corresponding to the CreateBottleCommand command. func (cmd *CreateBottleCommand) Run(c *client.Client, args []string) error { var path string if len(args) > 0 { path = args[0] } else { path = "/bottles" } var payload client.CreateBottlePayload if cmd.Payload != "" { err := json.Unmarshal([]byte(cmd.Payload), &payload) if err != nil { return fmt.Errorf("failed to deserialize payload: %s", err) } } logger := goa.NewLogger(log.New(os.Stderr, "", log.LstdFlags)) ctx := goa.WithLogger(context.Background(), logger) resp, err := c.CreateBottle(ctx, path, &payload, cmd.ContentType) if err != nil { goa.LogError(ctx, "failed", "err", err) return err } goaclient.HandleResponse(c.Client, resp, cmd.PrettyPrint) return nil }
// ErrorHandler turns a Go error into an HTTP response. It should be placed in the middleware chain // below the logger middleware so the logger properly logs the HTTP response. ErrorHandler // understands instances of goa.Error and returns the status and response body embodied in them, // it turns other Go error types into a 500 internal error response. // If verbose is false the details of internal errors is not included in HTTP responses. func ErrorHandler(service *goa.Service, verbose bool) goa.Middleware { return func(h goa.Handler) goa.Handler { return func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { e := h(ctx, rw, req) if e == nil { return nil } status := http.StatusInternalServerError var respBody interface{} if err, ok := e.(*goa.Error); ok { status = err.Status respBody = err goa.ContextResponse(ctx).ErrorCode = err.Code rw.Header().Set("Content-Type", goa.ErrorMediaIdentifier) } else { respBody = e.Error() rw.Header().Set("Content-Type", "text/plain") } if status >= 500 && status < 600 { reqID := ctx.Value(reqIDKey) if reqID == nil { reqID = shortID() ctx = context.WithValue(ctx, reqIDKey, reqID) } goa.LogError(ctx, "uncaught error", "id", reqID, "msg", respBody) if !verbose { rw.Header().Set("Content-Type", goa.ErrorMediaIdentifier) respBody = goa.ErrInternal("internal error [%s]", reqID) } } return service.Send(ctx, status, respBody) } } }
// Do wraps the underlying http client Do method and adds logging. // The logger should be in the context. func (c *Client) Do(ctx context.Context, req *http.Request) (*http.Response, error) { // TODO: setting the request ID should be done via client middleware. For now only set it if the // caller provided one in the ctx. if ctxreqid := ContextRequestID(ctx); ctxreqid != "" { req.Header.Set("X-Request-Id", ctxreqid) } if c.UserAgent != "" { req.Header.Set("User-Agent", c.UserAgent) } startedAt := time.Now() ctx, id := ContextWithRequestID(ctx) goa.LogInfo(ctx, "started", "id", id, req.Method, req.URL.String()) if c.Dump { c.dumpRequest(ctx, req) } resp, err := c.Doer.Do(ctx, req) if err != nil { goa.LogError(ctx, "failed", "err", err) return nil, err } goa.LogInfo(ctx, "completed", "id", id, "status", resp.StatusCode, "time", time.Since(startedAt).String()) if c.Dump { c.dumpResponse(ctx, resp) } return resp, err }
// Refresh makes a OAuth2 refresh access token request. func (s *OAuth2Signer) Refresh(ctx context.Context) error { url := fmt.Sprintf(s.RefreshURLFormat, s.RefreshToken) req, err := http.NewRequest("POST", url, nil) if err != nil { return err } id := shortID() goa.LogInfo(ctx, "refresh", "id", id, "url", fmt.Sprintf(s.RefreshURLFormat, "*****")) resp, err := http.DefaultClient.Do(req) if err != nil { goa.LogError(ctx, "failed", "id", id, "err", err) return err } goa.LogInfo(ctx, "completed", "id", id, "status", resp.Status) defer resp.Body.Close() var r oauth2RefreshResponse respBody, err := ioutil.ReadAll(resp.Body) if err != nil { return fmt.Errorf("failed to read response body: %s", err) } err = json.Unmarshal(respBody, &r) if err != nil { return fmt.Errorf("failed to decode refresh request response: %s", err) } s.accessToken = r.AccessToken if r.ExpiresIn > 0 { s.expiresAt = time.Now().Add(time.Duration(r.ExpiresIn) * time.Second) goa.LogInfo(ctx, "refreshed", "expires", s.expiresAt) } if r.RefreshToken != "" { s.RefreshToken = r.RefreshToken } return nil }
// Dump request if needed. func (c *Client) dumpRequest(ctx context.Context, req *http.Request) { reqBody, err := dumpReqBody(req) if err != nil { goa.LogError(ctx, "Failed to load request body for dump", "err", err.Error()) } goa.LogInfo(ctx, "request headers", headersToSlice(req.Header)...) if reqBody != nil { goa.LogInfo(ctx, "request", "body", string(reqBody)) } }
// Create creates a new record. func (m *GormAreaRepository) Create(ctx context.Context, u *Area) error { defer goa.MeasureSince([]string{"goa", "db", "area", "create"}, time.Now()) u.ID = uuid.NewV4() err := m.db.Create(u).Error if err != nil { goa.LogError(ctx, "error adding Area", "error", err.Error()) return err } return nil }
// Run downloads files with given paths. func (cmd *DownloadCommand) Run(c *client.Client, args []string) error { var ( fnf func(context.Context, string) (int64, error) fnd func(context.Context, string, string) (int64, error) rpath = args[0] outfile = cmd.OutFile logger = goa.NewLogger(log.New(os.Stderr, "", log.LstdFlags)) ctx = goa.WithLogger(context.Background(), logger) err error ) if rpath[0] != '/' { rpath = "/" + rpath } if rpath == "/swagger.json" { fnf = c.DownloadSwaggerJSON if outfile == "" { outfile = "swagger.json" } goto found } if strings.HasPrefix(rpath, "/swagger-ui/") { fnd = c.DownloadSwaggerUI rpath = rpath[12:] if outfile == "" { _, outfile = path.Split(rpath) } goto found } return fmt.Errorf("don't know how to download %s", rpath) found: ctx = goa.WithLogContext(ctx, "file", outfile) if fnf != nil { _, err = fnf(ctx, outfile) } else { _, err = fnd(ctx, rpath, outfile) } if err != nil { goa.LogError(ctx, "failed", "err", err) return err } return nil }
// Run makes the HTTP request corresponding to the ShowBottleCommand command. func (cmd *ShowBottleCommand) Run(c *client.Client, args []string) error { var path string if len(args) > 0 { path = args[0] } else { path = fmt.Sprintf("/bottles/%v", cmd.ID) } logger := goa.NewLogger(log.New(os.Stderr, "", log.LstdFlags)) ctx := goa.WithLogger(context.Background(), logger) resp, err := c.ShowBottle(ctx, path) if err != nil { goa.LogError(ctx, "failed", "err", err) return err } goaclient.HandleResponse(c.Client, resp, cmd.PrettyPrint) return nil }
// Do wraps the underlying http client Do method and adds logging. // The logger should be in the context. func (c *Client) Do(ctx context.Context, req *http.Request) (*http.Response, error) { req.Header.Set("User-Agent", c.UserAgent) startedAt := time.Now() id := shortID() goa.LogInfo(ctx, "started", "id", id, req.Method, req.URL.String()) if c.Dump { c.dumpRequest(ctx, req) } resp, err := c.Client.Do(req) if err != nil { goa.LogError(ctx, "failed", "err", err) return nil, err } goa.LogInfo(ctx, "completed", "id", id, "status", resp.StatusCode, "time", time.Since(startedAt).String()) if c.Dump { c.dumpResponse(ctx, resp) } return resp, err }
// ErrorHandler turns a Go error into an HTTP response. It should be placed in the middleware chain // below the logger middleware so the logger properly logs the HTTP response. ErrorHandler // understands instances of goa.ServiceError and returns the status and response body embodied in // them, it turns other Go error types into a 500 internal error response. // If verbose is false the details of internal errors is not included in HTTP responses. // If you use github.com/pkg/errors then wrapping the error will allow a trace to be printed to the logs func ErrorHandler(service *goa.Service, verbose bool) goa.Middleware { return func(h goa.Handler) goa.Handler { return func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { e := h(ctx, rw, req) if e == nil { return nil } cause := errors.Cause(e) status := http.StatusInternalServerError var respBody interface{} if err, ok := cause.(goa.ServiceError); ok { status = err.ResponseStatus() respBody = err goa.ContextResponse(ctx).ErrorCode = err.Token() rw.Header().Set("Content-Type", goa.ErrorMediaIdentifier) } else { respBody = e.Error() rw.Header().Set("Content-Type", "text/plain") } if status >= 500 && status < 600 { reqID := ctx.Value(reqIDKey) if reqID == nil { reqID = shortID() ctx = context.WithValue(ctx, reqIDKey, reqID) } goa.LogError(ctx, "uncaught error", "err", fmt.Sprintf("%+v", e), "id", reqID, "msg", respBody) if !verbose { rw.Header().Set("Content-Type", goa.ErrorMediaIdentifier) msg := fmt.Sprintf("%s [%s]", http.StatusText(http.StatusInternalServerError), reqID) respBody = goa.ErrInternal(msg) // Preserve the ID of the original error as that's what gets logged, the client // received error ID must match the original if origErrID := goa.ContextResponse(ctx).ErrorCode; origErrID != "" { respBody.(*goa.ErrorResponse).ID = origErrID } } } return service.Send(ctx, status, respBody) } } }
// New returns a middleware to be used with the JWTSecurity DSL definitions of goa. It supports the // scopes claim in the JWT and ensures goa-defined Security DSLs are properly validated. // // The steps taken by the middleware are: // // 1. Validate the "Bearer" token present in the "Authorization" header against the key(s) // given to New // 2. If scopes are defined in the design for the action validate them against the "scopes" JWT // claim // // The `exp` (expiration) and `nbf` (not before) date checks are validated by the JWT library. // // validationKeys can be one of these: // // * a single string // * a single []byte // * a list of string // * a list of []byte // * a single rsa.PublicKey // * a list of rsa.PublicKey // // The type of the keys determine the algorithms that will be used to do the check. The goal of // having lists of keys is to allow for key rotation, still check the previous keys until rotation // has been completed. // // You can define an optional function to do additional validations on the token once the signature // and the claims requirements are proven to be valid. Example: // // validationHandler, _ := goa.NewMiddleware(func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { // token := jwt.ContextJWT(ctx) // if val, ok := token.Claims["is_uncle"].(string); !ok || val != "ben" { // return jwt.ErrJWTError.Errorf("you are not uncle ben's") // } // }) // app.MyJWTSecurity.Use(jwt.New("secret", validationHandler)) // func New(validationKeys interface{}, validationFunc goa.Middleware) goa.JWTSecurityConfigFunc { return func(scheme *goa.JWTSecurity, getScopes func(context.Context) []string) goa.Middleware { var algo string var rsaKeys []*rsa.PublicKey var hmacKeys []string switch keys := validationKeys.(type) { case []*rsa.PublicKey: rsaKeys = keys algo = "RS" case *rsa.PublicKey: rsaKeys = []*rsa.PublicKey{keys} algo = "RS" case string: hmacKeys = []string{keys} algo = "HS" case []string: hmacKeys = keys algo = "HS" default: panic("invalid parameter to `jwt.New()`, only accepts *rsa.publicKey, []*rsa.PublicKey (for RSA-based algorithms) or a signing secret string (for HS algorithms)") } return func(nextHandler goa.Handler) goa.Handler { return func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { // TODO: implement the QUERY string handler too if scheme.In != goa.LocHeader { return fmt.Errorf("whoops, security scheme with in = %q not supported", scheme.In) } val := req.Header.Get(scheme.Name) if val == "" { return ErrJWTError("missing header %q", scheme.Name) } if !strings.HasPrefix(strings.ToLower(val), "bearer ") { return ErrJWTError("invalid or malformed %q header, expected 'Authorization: Bearer JWT-token...'", val) } incomingToken := strings.Split(val, " ")[1] var token *jwt.Token var err error switch algo { case "RS": token, err = validateRSAKeys(rsaKeys, algo, incomingToken) case "HS": token, err = validateHMACKeys(hmacKeys, algo, incomingToken) default: panic("how did this happen ? unsupported algo in jwt middleware") } if err != nil { return ErrJWTError("JWT validation failed") } scopesInClaim, scopesInClaimList, err := parseClaimScopes(token) if err != nil { goa.LogError(ctx, err.Error()) return ErrJWTError(err) } requiredScopes := getScopes(ctx) for _, scope := range requiredScopes { if !scopesInClaim[scope] { return ErrJWTError("authorization failed: required 'scopes' not present in JWT claim").Meta("required_scopes", requiredScopes, "scopes_in_claim", scopesInClaimList) } } ctx = context.WithValue(ctx, jwtKey, token) if validationFunc != nil { nextHandler = validationFunc(nextHandler) } return nextHandler(ctx, rw, req) } } } }
. "github.com/onsi/gomega" "golang.org/x/net/context" ) var _ = Describe("Info", func() { Context("with a nil Log", func() { It("doesn't log and doesn't crash", func() { Ω(func() { goa.LogInfo(context.Background(), "foo", "bar") }).ShouldNot(Panic()) }) }) }) var _ = Describe("Error", func() { Context("with a nil Log", func() { It("doesn't log and doesn't crash", func() { Ω(func() { goa.LogError(context.Background(), "foo", "bar") }).ShouldNot(Panic()) }) }) }) var _ = Describe("LogAdapter", func() { Context("with a valid Log", func() { var logger goa.LogAdapter const msg = "message" data := []interface{}{"data", "foo"} var out bytes.Buffer BeforeEach(func() { stdlogger := log.New(&out, "", log.LstdFlags) logger = goa.NewLogger(stdlogger)
// New returns a middleware to be used with the JWTSecurity DSL definitions of goa. It supports the // scopes claim in the JWT and ensures goa-defined Security DSLs are properly validated. // // The steps taken by the middleware are: // // 1. Validate the "Bearer" token present in the "Authorization" header against the key(s) // given to New // 2. If scopes are defined in the design for the action validate them against the "scopes" JWT // claim // // The `exp` (expiration) and `nbf` (not before) date checks are validated by the JWT library. // // validationKeys can be one of these: // // * a single []byte // * a single string // * a slice of []byte // * a slice of string // * a single *rsa.PublicKey // * a slice of *rsa.PublicKey // // Keys of type string or []byte are interepreted according to the signing method defined in the JWT // token (HMAC, RSA, etc.). // // You can define an optional function to do additional validations on the token once the signature // and the claims requirements are proven to be valid. Example: // // validationHandler, _ := goa.NewMiddleware(func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { // token := jwt.ContextJWT(ctx) // if val, ok := token.Claims["is_uncle"].(string); !ok || val != "ben" { // return jwt.ErrJWTError("you are not uncle ben's") // } // }) // // Mount the middleware with the generated UseXX function where XX is the name of the scheme as // defined in the design, e.g.: // // jwtResolver, _ := jwt.NewSimpleResolver("secret") // app.UseJWT(jwt.New(jwtResolver, validationHandler, app.NewJWTSecurity())) // func New(resolver KeyResolver, validationFunc goa.Middleware, scheme *goa.JWTSecurity) goa.Middleware { return func(nextHandler goa.Handler) goa.Handler { return func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error { // TODO: implement the QUERY string handler too if scheme.In != goa.LocHeader { return fmt.Errorf("whoops, security scheme with location (in) %q not supported", scheme.In) } val := req.Header.Get(scheme.Name) if val == "" { return ErrJWTError(fmt.Sprintf("missing header %q", scheme.Name)) } if !strings.HasPrefix(strings.ToLower(val), "bearer ") { return ErrJWTError(fmt.Sprintf("invalid or malformed %q header, expected 'Authorization: Bearer JWT-token...'", val)) } incomingToken := strings.Split(val, " ")[1] var ( rsaKeys []*rsa.PublicKey hmacKeys [][]byte keys = resolver.SelectKeys(req) ) { for _, key := range keys { switch k := key.(type) { case *rsa.PublicKey: rsaKeys = append(rsaKeys, k) case []byte: hmacKeys = append(hmacKeys, k) case string: hmacKeys = append(hmacKeys, []byte(k)) } } } var ( token *jwt.Token err error validated = false ) if len(rsaKeys) > 0 { token, err = validateRSAKeys(rsaKeys, "RS", incomingToken) if err == nil { validated = true } } if !validated && len(hmacKeys) > 0 { token, err = validateHMACKeys(hmacKeys, "HS", incomingToken) if err == nil { validated = true } } if !validated { return ErrJWTError("JWT validation failed") } scopesInClaim, scopesInClaimList, err := parseClaimScopes(token) if err != nil { goa.LogError(ctx, err.Error()) return ErrJWTError(err) } requiredScopes := goa.ContextRequiredScopes(ctx) for _, scope := range requiredScopes { if !scopesInClaim[scope] { msg := "authorization failed: required 'scopes' not present in JWT claim" return ErrJWTError(msg, "required", requiredScopes, "scopes", scopesInClaimList) } } ctx = WithJWT(ctx, token) if validationFunc != nil { nextHandler = validationFunc(nextHandler) } return nextHandler(ctx, rw, req) } } }