diff --git a/ginserver/logger.go b/ginserver/logger.go index 9334a43..50a1f3a 100755 --- a/ginserver/logger.go +++ b/ginserver/logger.go @@ -2,7 +2,7 @@ package ginserver import ( "math/rand" - utils2 "nos-comptes/internal/utils" + "nos-comptes/internal/utils" "time" "github.com/gin-gonic/gin" @@ -37,16 +37,16 @@ func randStringBytesMaskImprSrc(n int) string { func GetLoggerMiddleware() gin.HandlerFunc { return func(c *gin.Context) { - correlationID := c.Request.Header.Get(utils2.HeaderNameCorrelationID) + correlationID := c.Request.Header.Get(utils.HeaderNameCorrelationID) if correlationID == "" { correlationID = randStringBytesMaskImprSrc(30) - c.Writer.Header().Set(utils2.HeaderNameCorrelationID, correlationID) + c.Writer.Header().Set(utils.HeaderNameCorrelationID, correlationID) } - logger := utils2.GetLogger() - logEntry := logger.WithField(utils2.HeaderNameCorrelationID, correlationID) + logger := utils.GetLogger() + 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) { start := time.Now() - utils2.GetLoggerFromCtx(c). + utils.GetLoggerFromCtx(c). WithField("method", c.Request.Method). WithField("url", c.Request.RequestURI). WithField("from", c.ClientIP()). @@ -63,7 +63,7 @@ func GetHTTPLoggerMiddleware() gin.HandlerFunc { c.Next() d := time.Since(start) - utils2.GetLoggerFromCtx(c). + utils.GetLoggerFromCtx(c). WithField("status", c.Writer.Status()). WithField("duration", d.String()). Info("end handling HTTP request") diff --git a/ginserver/router.go b/ginserver/router.go index e72f5b4..1324d76 100644 --- a/ginserver/router.go +++ b/ginserver/router.go @@ -40,7 +40,7 @@ func NewRouter(config *handler.Config) *gin.Engine { ah := account.NewHandler(hc, db) sah := sharedaccount.NewHandler(hc, db) eh := expense.NewHandler(hc, db) - + mv := middleware.NewValidator(hc, db) public := router.Group("/") 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(http.MethodPost, "", uh.CreateUser) - securedUserRoute := userRoute.Group("") + securedUserRoute := userRoute.Group("/") securedUserRoute.Use(middleware.ValidateOAuthToken) //TODO add secure auth securedUserRoute.Handle(http.MethodGet, "/:userId", uh.GetUser) + securedMatchingToken := securedUserRoute.Group("/:userId") + securedMatchingToken.Use(mv.HasValidUserId) + securedMatchingToken.Use(mv.UserdIdMatchOAuthToken) //account route - securedUserRoute.Handle(http.MethodGet, "/:userId/accounts", ah.GetAllAccountOfUser) - securedUserRoute.Handle(http.MethodPost, "/:userId/accounts", ah.CreateAccountOfUser) - securedUserRoute.Handle(http.MethodDelete, "/:userId/accounts/:accountId", ah.DeleteAccountOfUser) - securedUserRoute.Handle(http.MethodGet, "/:userId/accounts/:accountId", ah.GetSpecificAccountOfUser) + securedMatchingToken.Handle(http.MethodGet, "/accounts", ah.GetAllAccountOfUser) + securedMatchingToken.Handle(http.MethodPost, "/accounts", ah.CreateAccountOfUser) + 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 securedUserRoute.Handle(http.MethodPost, "/:userId/sharedaccounts/:accountId", sah.ShareAnAccount) securedUserRoute.Handle(http.MethodDelete, "/:userId/sharedaccounts/:accountId", sah.DeleteSharedAccount) securedUserRoute.Handle(http.MethodGet, "/:userId/sharedaccounts", sah.GetAllSharedAccountOfUser) 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 } diff --git a/internal/account/handler.go b/internal/account/handler.go index 231fcdd..f9981b8 100644 --- a/internal/account/handler.go +++ b/internal/account/handler.go @@ -8,7 +8,6 @@ import ( "nos-comptes/internal/storage/validators" "nos-comptes/internal/user" "nos-comptes/internal/utils" - utils2 "nos-comptes/internal/utils" "github.com/gin-gonic/gin" ) @@ -22,22 +21,6 @@ type Context struct { func (c *Context) GetAllAccountOfUser(gc *gin.Context) { 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) if e, ok := err.(*model.APIError); ok { 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) { 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 accountEditable AccountEditable if err := gc.BindJSON(&accountEditable); err != nil { - utils2.JSONError(gc.Writer, validators.NewDataValidationAPIError(err)) + utils.JSONError(gc.Writer, validators.NewDataValidationAPIError(err)) return } account = Account{AccountEditable: accountEditable, UserId: userId} @@ -112,122 +80,16 @@ func (c *Context) CreateAccountOfUser(gc *gin.Context) { func (c *Context) DeleteAccountOfUser(gc *gin.Context) { 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") - 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) } func (c *Context) GetSpecificAccountOfUser(gc *gin.Context) { 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") - 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 - } - - 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) + account, _ := c.service.GetASpecificAccountForUser(userId, accountId) + utils.JSON(gc.Writer, http.StatusOK, account) } func NewHandler(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Context { diff --git a/internal/user/handler.go b/internal/user/handler.go index 72e2c0c..16e7dfc 100755 --- a/internal/user/handler.go +++ b/internal/user/handler.go @@ -6,7 +6,6 @@ import ( "nos-comptes/handler" "nos-comptes/internal/storage/dao/postgresql" "nos-comptes/internal/storage/model" - "nos-comptes/internal/storage/validators" "nos-comptes/internal/utils" "strings" @@ -78,8 +77,6 @@ func (hc *Context) ConnectUser(c *gin.Context) { utils.JSONError(c.Writer, model.ErrInternalServer) return } - fmt.Println("Found the user " + user.Email) - fmt.Println("Return 200") utils.JSON(c.Writer, 200, user) } @@ -130,27 +127,6 @@ func (hc *Context) CreateUser(c *gin.Context) { func (hc *Context) GetUser(c *gin.Context) { userID := c.Param("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 - } - + user, _ := hc.service.GetUserById(userID) utils.JSON(c.Writer, http.StatusOK, user) } diff --git a/middleware/validator.go b/middleware/validator.go new file mode 100644 index 0000000..d6c2333 --- /dev/null +++ b/middleware/validator.go @@ -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 + } +}