ソースを参照

added validators and updated username -> email

Alexander Belanger 5 年 前
コミット
5c07629bef
8 ファイル変更86 行追加14 行削除
  1. 6 3
      cmd/app/main.go
  2. 5 0
      go.mod
  3. 12 0
      go.sum
  4. 7 7
      internal/models/user.go
  5. 11 0
      internal/validator/validator.go
  6. 18 4
      server/api/api.go
  7. 22 0
      server/api/errors.go
  8. 5 0
      server/api/user_handler.go

+ 6 - 3
cmd/app/main.go

@@ -7,21 +7,24 @@ import (
 
 	"github.com/porter-dev/porter/server/api"
 
-	dbConn "github.com/porter-dev/porter/internal/gorm"
+	adapter "github.com/porter-dev/porter/internal/adapter"
 	lr "github.com/porter-dev/porter/internal/logger"
+	vr "github.com/porter-dev/porter/internal/validator"
 	"github.com/porter-dev/porter/server/router"
 )
 
 func main() {
 	logger := lr.NewConsole(true)
-	db, err := dbConn.New()
+	db, err := adapter.New()
 
 	if err != nil {
 		logger.Fatal().Err(err).Msg("")
 		return
 	}
 
-	a := api.New(logger, db)
+	validator := vr.New()
+
+	a := api.New(logger, db, validator)
 
 	appRouter := router.New(a)
 

+ 5 - 0
go.mod

@@ -4,13 +4,18 @@ go 1.14
 
 require (
 	github.com/go-chi/chi v4.1.2+incompatible
+	github.com/go-playground/locales v0.13.0
+	github.com/go-playground/universal-translator v0.17.0
+	github.com/go-playground/validator/v10 v10.3.0
 	github.com/imdario/mergo v0.3.11 // indirect
 	github.com/jinzhu/gorm v1.9.16
+	github.com/leodido/go-urn v1.2.0 // indirect
 	github.com/rs/zerolog v1.20.0
 	golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a // indirect
 	golang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect
 	golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 // indirect
 	golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect
+	gopkg.in/go-playground/validator.v9 v9.31.0
 	gopkg.in/yaml.v2 v2.3.0
 	gorm.io/driver/postgres v1.0.1
 	gorm.io/gorm v1.20.1

+ 12 - 0
go.sum

@@ -87,6 +87,14 @@ github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+
 github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
 github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
 github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
+github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
+github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/validator v9.31.0+incompatible h1:UA72EPEogEnq76ehGdEDp4Mit+3FDh548oRqwVgNsHA=
+github.com/go-playground/validator/v10 v10.3.0 h1:nZU+7q+yJoFmwvNgv/LnPUkwPal62+b2xXj0AU1Es7o=
+github.com/go-playground/validator/v10 v10.3.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
 github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
@@ -224,6 +232,8 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
+github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
@@ -565,6 +575,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/go-playground/validator.v9 v9.31.0 h1:bmXmP2RSNtFES+bn4uYuHT7iJFJv7Vj+an+ZQdDaD1M=
+gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
 gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
 gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=

+ 7 - 7
internal/models/user.go

@@ -7,8 +7,8 @@ import (
 // User type that extends gorm.Model
 type User struct {
 	gorm.Model
-	// Unique username for each user
-	Username,
+	// Unique email for each user
+	Email,
 	// Hashed password
 	Password string
 	// The clusters that this user has linked
@@ -20,15 +20,15 @@ type User struct {
 // UserExternal represents the User type that is sent over REST
 type UserExternal struct {
 	ID            uint                     `json:"id"`
-	Username      string                   `json:"username"`
+	Email         string                   `json:"email"`
 	Clusters      []*ClusterConfigExternal `json:"clusters"`
 	RawKubeConfig string                   `json:"rawKubeConfig"`
 }
 
 // CreateUserForm represents the accepted values for creating a user
 type CreateUserForm struct {
-	Username string `json:"username"`
-	Password string `json:"password"`
+	Email    string `json:"email" form:"required,max=255,email"`
+	Password string `json:"password" form:"required,max=255"`
 }
 
 // UpdateUserForm represents the accepted values for updating a user
@@ -46,7 +46,7 @@ func (u *User) Externalize() *UserExternal {
 
 	return &UserExternal{
 		ID:            u.ID,
-		Username:      u.Username,
+		Email:         u.Email,
 		Clusters:      clustersExt,
 		RawKubeConfig: string(u.RawKubeConfig),
 	}
@@ -57,7 +57,7 @@ func (u *User) Externalize() *UserExternal {
 // TODO -- PASSWORD HASHING HERE
 func (cuf *CreateUserForm) ToUser() (*User, error) {
 	return &User{
-		Username: cuf.Username,
+		Email:    cuf.Email,
 		Password: cuf.Password,
 	}, nil
 }

+ 11 - 0
internal/validator/validator.go

@@ -0,0 +1,11 @@
+package validator
+
+import (
+	"gopkg.in/go-playground/validator.v9"
+)
+
+func New() *validator.Validate {
+	validate := validator.New()
+	validate.SetTagName("form")
+	return validate
+}

+ 18 - 4
server/api/api.go

@@ -1,6 +1,9 @@
 package api
 
 import (
+	"github.com/go-playground/locales/en"
+	ut "github.com/go-playground/universal-translator"
+	"github.com/go-playground/validator/v10"
 	lr "github.com/porter-dev/porter/internal/logger"
 	"gorm.io/gorm"
 )
@@ -8,18 +11,29 @@ import (
 // App represents an API instance with handler methods attached, a DB connection
 // and a logger instance
 type App struct {
-	logger *lr.Logger
-	db     *gorm.DB
+	logger     *lr.Logger
+	db         *gorm.DB
+	validator  *validator.Validate
+	translator *ut.Translator
 }
 
 // New returns a new App instance
 func New(
 	logger *lr.Logger,
 	db *gorm.DB,
+	validator *validator.Validate,
 ) *App {
+	// for now, will just support the english translator from the
+	// validator/translations package
+	en := en.New()
+	uni := ut.New(en, en)
+	trans, _ := uni.GetTranslator("en")
+
 	return &App{
-		logger: logger,
-		db:     db,
+		logger:     logger,
+		db:         db,
+		validator:  validator,
+		translator: &trans,
 	}
 }
 

+ 22 - 0
server/api/errors.go

@@ -1,8 +1,11 @@
 package api
 
 import (
+	"encoding/json"
 	"fmt"
 	"net/http"
+
+	"gopkg.in/go-playground/validator.v9"
 )
 
 const (
@@ -16,8 +19,27 @@ func (app *App) handleUnprocessableEntity(err error, w http.ResponseWriter) {
 	fmt.Fprintf(w, `{"error": "%v"}`, appErrFormDecodingFailure)
 }
 
+func (app *App) handleErrorFormValidation(err error, w http.ResponseWriter) {
+	// translate all validator errors
+	errs := err.(validator.ValidationErrors)
+	translation := errs.Translate(*app.translator)
+	respBody, newErr := json.Marshal(translation)
+
+	if newErr != nil {
+		app.handleGenericInternalError(newErr, w)
+	}
+
+	fmt.Fprintf(w, `{"errors": %v}`, respBody)
+}
+
 func (app *App) handleDataWriteFailure(err error, w http.ResponseWriter) {
 	app.logger.Warn().Err(err).Msg("")
 	w.WriteHeader(http.StatusInternalServerError)
 	fmt.Fprintf(w, `{"error": "%v"}`, appErrDataCreationFailure)
 }
+
+func (app *App) handleGenericInternalError(err error, w http.ResponseWriter) {
+	app.logger.Warn().Err(err).Msg("")
+	w.WriteHeader(http.StatusInternalServerError)
+	fmt.Fprintf(w, `{"error": "Internal server error"}`)
+}

+ 5 - 0
server/api/user_handler.go

@@ -19,6 +19,11 @@ func (app *App) HandleCreateUser(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	if err := app.validator.Struct(form); err != nil {
+		app.handleUnprocessableEntity(err, w)
+		return
+	}
+
 	userModel, err := form.ToUser()
 
 	if err != nil {