refactor: now validate request with middleware

This commit is contained in:
2021-11-24 00:51:55 +01:00
parent 4035478c54
commit 917c3a4318
5 changed files with 141 additions and 185 deletions

View File

@@ -2,7 +2,7 @@ package ginserver
import ( import (
"math/rand" "math/rand"
utils2 "nos-comptes/internal/utils" "nos-comptes/internal/utils"
"time" "time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -37,16 +37,16 @@ func randStringBytesMaskImprSrc(n int) string {
func GetLoggerMiddleware() gin.HandlerFunc { func GetLoggerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
correlationID := c.Request.Header.Get(utils2.HeaderNameCorrelationID) correlationID := c.Request.Header.Get(utils.HeaderNameCorrelationID)
if correlationID == "" { if correlationID == "" {
correlationID = randStringBytesMaskImprSrc(30) correlationID = randStringBytesMaskImprSrc(30)
c.Writer.Header().Set(utils2.HeaderNameCorrelationID, correlationID) c.Writer.Header().Set(utils.HeaderNameCorrelationID, correlationID)
} }
logger := utils2.GetLogger() logger := utils.GetLogger()
logEntry := logger.WithField(utils2.HeaderNameCorrelationID, correlationID) logEntry := logger.WithField(utils.HeaderNameCorrelationID, correlationID)
c.Set(utils2.ContextKeyLogger, logEntry) c.Set(utils.ContextKeyLogger, logEntry)
} }
} }
@@ -54,7 +54,7 @@ func GetHTTPLoggerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
start := time.Now() start := time.Now()
utils2.GetLoggerFromCtx(c). utils.GetLoggerFromCtx(c).
WithField("method", c.Request.Method). WithField("method", c.Request.Method).
WithField("url", c.Request.RequestURI). WithField("url", c.Request.RequestURI).
WithField("from", c.ClientIP()). WithField("from", c.ClientIP()).
@@ -63,7 +63,7 @@ func GetHTTPLoggerMiddleware() gin.HandlerFunc {
c.Next() c.Next()
d := time.Since(start) d := time.Since(start)
utils2.GetLoggerFromCtx(c). utils.GetLoggerFromCtx(c).
WithField("status", c.Writer.Status()). WithField("status", c.Writer.Status()).
WithField("duration", d.String()). WithField("duration", d.String()).
Info("end handling HTTP request") Info("end handling HTTP request")

View File

@@ -40,7 +40,7 @@ func NewRouter(config *handler.Config) *gin.Engine {
ah := account.NewHandler(hc, db) ah := account.NewHandler(hc, db)
sah := sharedaccount.NewHandler(hc, db) sah := sharedaccount.NewHandler(hc, db)
eh := expense.NewHandler(hc, db) eh := expense.NewHandler(hc, db)
mv := middleware.NewValidator(hc, db)
public := router.Group("/") public := router.Group("/")
public.Handle(http.MethodGet, "/_health", hc.GetHealth) public.Handle(http.MethodGet, "/_health", hc.GetHealth)
@@ -48,26 +48,34 @@ func NewRouter(config *handler.Config) *gin.Engine {
userRoute.Handle("GET", "", uh.ConnectUser) userRoute.Handle("GET", "", uh.ConnectUser)
userRoute.Handle(http.MethodPost, "", uh.CreateUser) userRoute.Handle(http.MethodPost, "", uh.CreateUser)
securedUserRoute := userRoute.Group("") securedUserRoute := userRoute.Group("/")
securedUserRoute.Use(middleware.ValidateOAuthToken) securedUserRoute.Use(middleware.ValidateOAuthToken)
//TODO add secure auth //TODO add secure auth
securedUserRoute.Handle(http.MethodGet, "/:userId", uh.GetUser) securedUserRoute.Handle(http.MethodGet, "/:userId", uh.GetUser)
securedMatchingToken := securedUserRoute.Group("/:userId")
securedMatchingToken.Use(mv.HasValidUserId)
securedMatchingToken.Use(mv.UserdIdMatchOAuthToken)
//account route //account route
securedUserRoute.Handle(http.MethodGet, "/:userId/accounts", ah.GetAllAccountOfUser) securedMatchingToken.Handle(http.MethodGet, "/accounts", ah.GetAllAccountOfUser)
securedUserRoute.Handle(http.MethodPost, "/:userId/accounts", ah.CreateAccountOfUser) securedMatchingToken.Handle(http.MethodPost, "/accounts", ah.CreateAccountOfUser)
securedUserRoute.Handle(http.MethodDelete, "/:userId/accounts/:accountId", ah.DeleteAccountOfUser)
securedUserRoute.Handle(http.MethodGet, "/:userId/accounts/:accountId", ah.GetSpecificAccountOfUser)
securedValidAccount := securedMatchingToken.Group("/accounts/:accountId")
securedValidAccount.Use(mv.HasValidAccountId)
securedValidAccount.Use(mv.AccountExists)
securedValidAccount.Handle(http.MethodDelete, "", ah.DeleteAccountOfUser)
securedValidAccount.Handle(http.MethodGet, "", ah.GetSpecificAccountOfUser)
securedValidAccount.Handle(http.MethodPost, "/expenses", eh.CreateAnExpense)
securedValidAccount.Handle(http.MethodGet, "/expenses", eh.GetAllExpenses)
securedExistingExpenses := securedValidAccount.Group("/expenses/:expenseId")
securedExistingExpenses.Handle(http.MethodGet, "", eh.GetAnExpenses)
securedExistingExpenses.Handle(http.MethodDelete, "", eh.DeleteExpense)
//shared route //shared route
securedUserRoute.Handle(http.MethodPost, "/:userId/sharedaccounts/:accountId", sah.ShareAnAccount) securedUserRoute.Handle(http.MethodPost, "/:userId/sharedaccounts/:accountId", sah.ShareAnAccount)
securedUserRoute.Handle(http.MethodDelete, "/:userId/sharedaccounts/:accountId", sah.DeleteSharedAccount) securedUserRoute.Handle(http.MethodDelete, "/:userId/sharedaccounts/:accountId", sah.DeleteSharedAccount)
securedUserRoute.Handle(http.MethodGet, "/:userId/sharedaccounts", sah.GetAllSharedAccountOfUser) securedUserRoute.Handle(http.MethodGet, "/:userId/sharedaccounts", sah.GetAllSharedAccountOfUser)
securedUserRoute.Handle(http.MethodGet, "/:userId/sharedaccounts/:sharedAccountId", sah.GetSpecificSharedAccountOfUser) securedUserRoute.Handle(http.MethodGet, "/:userId/sharedaccounts/:sharedAccountId", sah.GetSpecificSharedAccountOfUser)
securedUserRoute.Handle(http.MethodPost, "/:userId/accounts/:accountId/expenses", eh.CreateAnExpense)
securedUserRoute.Handle(http.MethodDelete, "/:userId/accounts/:accountId/expenses/:expenseId", eh.DeleteExpense)
securedUserRoute.Handle(http.MethodGet, "/:userId/accounts/:accountId/expenses", eh.GetAllExpenses)
securedUserRoute.Handle(http.MethodGet, "/:userId/accounts/:accountId/expenses/:expenseId", eh.GetAnExpenses)
return router return router
} }

View File

@@ -8,7 +8,6 @@ import (
"nos-comptes/internal/storage/validators" "nos-comptes/internal/storage/validators"
"nos-comptes/internal/user" "nos-comptes/internal/user"
"nos-comptes/internal/utils" "nos-comptes/internal/utils"
utils2 "nos-comptes/internal/utils"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@@ -22,22 +21,6 @@ type Context struct {
func (c *Context) GetAllAccountOfUser(gc *gin.Context) { func (c *Context) GetAllAccountOfUser(gc *gin.Context) {
userId := gc.Param("userId") userId := gc.Param("userId")
err := c.Validator.VarCtx(gc, userId, "uuid4")
if err != nil {
utils2.JSONError(gc.Writer, validators.NewDataValidationAPIError(err))
return
}
_, err = c.userService.GetUserById(userId)
if e, ok := err.(*model.APIError); ok {
utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUser: get user error")
utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
} else if err != nil {
utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user")
utils.JSONError(gc.Writer, model.ErrInternalServer)
return
}
accounts, err := c.service.GetAllAccountOfUser(userId) accounts, err := c.service.GetAllAccountOfUser(userId)
if e, ok := err.(*model.APIError); ok { if e, ok := err.(*model.APIError); ok {
utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetAllAccounts: get accounts") utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetAllAccounts: get accounts")
@@ -58,26 +41,11 @@ func (c *Context) GetAllAccountOfUser(gc *gin.Context) {
func (c *Context) CreateAccountOfUser(gc *gin.Context) { func (c *Context) CreateAccountOfUser(gc *gin.Context) {
userId := gc.Param("userId") userId := gc.Param("userId")
err := c.Validator.VarCtx(gc, userId, "uuid4")
if err != nil {
utils2.JSONError(gc.Writer, validators.NewDataValidationAPIError(err))
return
}
_, err = c.userService.GetUserById(userId)
if e, ok := err.(*model.APIError); ok {
utils.GetLogger().Info(err)
utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUser: get user error")
utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
return
} else if err != nil {
utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user")
utils.JSONError(gc.Writer, model.ErrInternalServer)
return
}
var account Account var account Account
var accountEditable AccountEditable var accountEditable AccountEditable
if err := gc.BindJSON(&accountEditable); err != nil { if err := gc.BindJSON(&accountEditable); err != nil {
utils2.JSONError(gc.Writer, validators.NewDataValidationAPIError(err)) utils.JSONError(gc.Writer, validators.NewDataValidationAPIError(err))
return return
} }
account = Account{AccountEditable: accountEditable, UserId: userId} account = Account{AccountEditable: accountEditable, UserId: userId}
@@ -112,122 +80,16 @@ func (c *Context) CreateAccountOfUser(gc *gin.Context) {
func (c *Context) DeleteAccountOfUser(gc *gin.Context) { func (c *Context) DeleteAccountOfUser(gc *gin.Context) {
userId := gc.Param("userId") userId := gc.Param("userId")
err := c.Validator.VarCtx(gc, userId, "uuid4")
if err != nil {
utils2.JSONError(gc.Writer, validators.NewDataValidationAPIError(err))
return
}
accountId := gc.Param("accountId") accountId := gc.Param("accountId")
err = c.Validator.VarCtx(gc, userId, "uuid4")
if err != nil {
utils2.JSONError(gc.Writer, validators.NewDataValidationAPIError(err))
return
}
usrParam, err := c.userService.GetUserById(userId)
if e, ok := err.(*model.APIError); ok {
utils.GetLogger().Info(err)
utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUser: get user error")
utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
return
} else if err != nil {
utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user")
utils.JSONError(gc.Writer, model.ErrInternalServer)
return
}
googleUserId, exists := gc.Get("googleUserId")
if exists == false {
utils.GetLoggerFromCtx(gc).Error("error while getting google user id")
utils.JSONError(gc.Writer, model.ErrInternalServer)
return
}
usr, err := c.userService.GetUserFromGoogleID(googleUserId.(string))
if e, ok := err.(*model.APIError); ok {
utils.GetLogger().Info(err)
utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUserFromGoogleID: get user from google user id")
utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
return
} else if err != nil {
utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user from google user id")
utils.JSONError(gc.Writer, model.ErrInternalServer)
return
}
if usr == nil || usr.ID != usrParam.ID {
utils.GetLoggerFromCtx(gc).WithError(err).Error("User in path doesn't match authenticated user")
utils.JSONError(gc.Writer, model.ErrBadRequestFormat)
return
}
c.service.DeleteAccountOfUser(userId, accountId) c.service.DeleteAccountOfUser(userId, accountId)
} }
func (c *Context) GetSpecificAccountOfUser(gc *gin.Context) { func (c *Context) GetSpecificAccountOfUser(gc *gin.Context) {
userId := gc.Param("userId") userId := gc.Param("userId")
err := c.Validator.VarCtx(gc, userId, "uuid4")
if err != nil {
utils2.JSONError(gc.Writer, validators.NewDataValidationAPIError(err))
return
}
accountId := gc.Param("accountId") accountId := gc.Param("accountId")
err = c.Validator.VarCtx(gc, userId, "uuid4") account, _ := c.service.GetASpecificAccountForUser(userId, accountId)
if err != nil { utils.JSON(gc.Writer, http.StatusOK, account)
utils2.JSONError(gc.Writer, validators.NewDataValidationAPIError(err))
return
}
usrParam, err := c.userService.GetUserById(userId)
if e, ok := err.(*model.APIError); ok {
utils.GetLogger().Info(err)
utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUser: get user error")
utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
return
} else if err != nil {
utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user")
utils.JSONError(gc.Writer, model.ErrInternalServer)
return
}
googleUserId, exists := gc.Get("googleUserId")
if exists == false {
utils.GetLoggerFromCtx(gc).Error("error while getting google user id")
utils.JSONError(gc.Writer, model.ErrInternalServer)
return
}
usr, err := c.userService.GetUserFromGoogleID(googleUserId.(string))
if e, ok := err.(*model.APIError); ok {
utils.GetLogger().Info(err)
utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUserFromGoogleID: get user from google user id")
utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
return
} else if err != nil {
utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user from google user id")
utils.JSONError(gc.Writer, model.ErrInternalServer)
return
}
if usr == nil || usr.ID != usrParam.ID {
utils.GetLoggerFromCtx(gc).WithError(err).Error("User in path doesn't match authenticated user")
utils.JSONError(gc.Writer, model.ErrBadRequestFormat)
return
}
account, err := c.service.GetASpecificAccountForUser(usr.ID, accountId)
if e, ok := err.(*model.APIError); ok {
utils.GetLogger().Info(err)
utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUserFromGoogleID: get user from google user id")
utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
return
} else if err != nil {
utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user from google user id")
utils.JSONError(gc.Writer, model.ErrInternalServer)
return
}
utils.JSON(gc.Writer, http.StatusCreated, account)
} }
func NewHandler(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Context { func NewHandler(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Context {

View File

@@ -6,7 +6,6 @@ import (
"nos-comptes/handler" "nos-comptes/handler"
"nos-comptes/internal/storage/dao/postgresql" "nos-comptes/internal/storage/dao/postgresql"
"nos-comptes/internal/storage/model" "nos-comptes/internal/storage/model"
"nos-comptes/internal/storage/validators"
"nos-comptes/internal/utils" "nos-comptes/internal/utils"
"strings" "strings"
@@ -78,8 +77,6 @@ func (hc *Context) ConnectUser(c *gin.Context) {
utils.JSONError(c.Writer, model.ErrInternalServer) utils.JSONError(c.Writer, model.ErrInternalServer)
return return
} }
fmt.Println("Found the user " + user.Email)
fmt.Println("Return 200")
utils.JSON(c.Writer, 200, user) utils.JSON(c.Writer, 200, user)
} }
@@ -130,27 +127,6 @@ func (hc *Context) CreateUser(c *gin.Context) {
func (hc *Context) GetUser(c *gin.Context) { func (hc *Context) GetUser(c *gin.Context) {
userID := c.Param("userId") userID := c.Param("userId")
user, _ := hc.service.GetUserById(userID)
err := hc.Validator.VarCtx(c, userID, "uuid4")
if err != nil {
utils.JSONError(c.Writer, validators.NewDataValidationAPIError(err))
return
}
user, err := hc.service.GetUserById(userID)
if e, ok := err.(*model.APIError); ok {
utils.GetLoggerFromCtx(c).WithError(err).WithField("type", e.Type).Error("error GetUser: get user error")
utils.JSONErrorWithMessage(c.Writer, *e, e.Description)
} else if err != nil {
utils.GetLoggerFromCtx(c).WithError(err).Error("error while get user")
utils.JSONError(c.Writer, model.ErrInternalServer)
return
}
if user == nil {
utils.JSONErrorWithMessage(c.Writer, model.ErrNotFound, "User not found")
return
}
utils.JSON(c.Writer, http.StatusOK, user) utils.JSON(c.Writer, http.StatusOK, user)
} }

110
middleware/validator.go Normal file
View File

@@ -0,0 +1,110 @@
package middleware
import (
"nos-comptes/handler"
"nos-comptes/internal/account"
"nos-comptes/internal/storage/dao/postgresql"
"nos-comptes/internal/storage/model"
"nos-comptes/internal/storage/validators"
"nos-comptes/internal/user"
"nos-comptes/internal/utils"
"github.com/gin-gonic/gin"
)
type Validator struct {
accountService *account.Service
userService *user.Service
*handler.Context
}
func NewValidator(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Validator {
return &Validator{accountService: account.NewService(account.NewDatabase(db)), userService: user.NewService(user.NewDatabase(db)), Context: ctx}
}
func (v Validator) HasValidUserId(gc *gin.Context) {
userId := gc.Param("userId")
err := v.Validator.VarCtx(gc, userId, "uuid4")
if err != nil {
utils.JSONError(gc.Writer, validators.NewDataValidationAPIError(err))
return
}
}
func (v Validator) UserExists(gc *gin.Context) {
userId := gc.Param("userId")
user, err := v.userService.GetUserById(userId)
if e, ok := err.(*model.APIError); ok {
utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUser: get user error")
utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
} else if err != nil {
utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user")
utils.JSONError(gc.Writer, model.ErrInternalServer)
return
}
if user == nil {
utils.JSONErrorWithMessage(gc.Writer, model.ErrNotFound, "User not found")
return
}
}
func (v Validator) UserdIdMatchOAuthToken(gc *gin.Context) {
userId := gc.Param("userId")
usrParam, err := v.userService.GetUserById(userId)
if e, ok := err.(*model.APIError); ok {
utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUser: get user error")
utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
} else if err != nil {
utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user")
utils.JSONError(gc.Writer, model.ErrInternalServer)
return
}
googleUserId, exists := gc.Get("googleUserId")
if exists == false {
utils.GetLoggerFromCtx(gc).Error("error while getting google user id")
utils.JSONError(gc.Writer, model.ErrInternalServer)
return
}
usr, err := v.userService.GetUserFromGoogleID(googleUserId.(string))
if e, ok := err.(*model.APIError); ok {
utils.GetLogger().Info(err)
utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUserFromGoogleID: get user from google user id")
utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
return
} else if err != nil {
utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user from google user id")
utils.JSONError(gc.Writer, model.ErrInternalServer)
return
}
if usr == nil || usr.ID != usrParam.ID {
utils.GetLoggerFromCtx(gc).WithError(err).Error("User in path doesn't match authenticated user")
utils.JSONError(gc.Writer, model.ErrBadRequestFormat)
return
}
}
func (v Validator) HasValidAccountId(gc *gin.Context) {
accountId := gc.Param("accountId")
err := v.Validator.VarCtx(gc, accountId, "uuid4")
if err != nil {
utils.JSONError(gc.Writer, validators.NewDataValidationAPIError(err))
return
}
}
func (v Validator) AccountExists(gc *gin.Context) {
userId := gc.Param("userId")
accountId := gc.Param("accountId")
_, err := v.accountService.GetASpecificAccountForUser(userId, accountId)
if e, ok := err.(*model.APIError); ok {
utils.GetLogger().Info(err)
utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUserFromGoogleID: get user from google user id")
utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
return
} else if err != nil {
utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user from google user id")
utils.JSONError(gc.Writer, model.ErrInternalServer)
return
}
}