From cbcfb8a2860fdc1e7f79da6adaab17290620326a Mon Sep 17 00:00:00 2001 From: Nikolai Papin Date: Tue, 24 Jun 2025 17:31:48 +0300 Subject: [PATCH] feat: middleware for request body parsing, validation and authentication; feat: helper function for getting request info from gin context --- backend/internal/controllers/auth.go | 15 ++++++----- backend/internal/middleware/request.go | 16 ++++++++++-- backend/internal/models/auth.go | 2 +- backend/internal/utils/request.go | 35 ++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 backend/internal/utils/request.go diff --git a/backend/internal/controllers/auth.go b/backend/internal/controllers/auth.go index 6a93e33..8e79fe6 100644 --- a/backend/internal/controllers/auth.go +++ b/backend/internal/controllers/auth.go @@ -21,10 +21,12 @@ import ( "easywish/internal/middleware" "easywish/internal/models" "easywish/internal/services" + "easywish/internal/utils" "easywish/internal/utils/enums" "net/http" "github.com/gin-gonic/gin" + "go.uber.org/zap" ) type AuthController interface { @@ -39,10 +41,11 @@ type AuthController interface { type authControllerImpl struct { authService services.AuthService + log *zap.Logger } -func NewAuthController(as services.AuthService) AuthController { - return &authControllerImpl{authService: as} +func NewAuthController(_log *zap.Logger, as services.AuthService) AuthController { + return &authControllerImpl{log: _log, authService: as} } // Login implements AuthController. @@ -96,14 +99,14 @@ func (a *authControllerImpl) Refresh(c *gin.Context) { // @Router /auth/registrationBegin [post] func (a *authControllerImpl) RegistrationBegin(c *gin.Context) { - var request models.RegistrationBeginRequest - - if err := c.ShouldBindJSON(&request); err != nil { + request, ok := utils.GetRequest[models.RegistrationBeginRequest](c) + if !ok { c.Status(http.StatusBadRequest) return } - _, err := a.authService.RegistrationBegin(request) + _, err := a.authService.RegistrationBegin(request.Body) + if err != nil { c.JSON(http.StatusBadRequest, err.Error()) return diff --git a/backend/internal/middleware/request.go b/backend/internal/middleware/request.go index fb33d52..29c50f4 100644 --- a/backend/internal/middleware/request.go +++ b/backend/internal/middleware/request.go @@ -43,13 +43,21 @@ func UserInfoFromContext(c *gin.Context) (*UserInfo, bool) { var ok bool username, ok = c.Get("username") ; if !ok { - return nil, true + return &UserInfo{Username: "", Role: enums.GuestRole}, true } role, ok = c.Get("role"); if !ok { return nil, false } + if username == nil { + return &UserInfo{Username: "", Role: enums.GuestRole}, true + } + + if role == nil { + return nil, false + } + return &UserInfo{Username: username.(string), Role: role.(enums.Role)}, true } @@ -67,9 +75,13 @@ func RequestMiddleware[T any](role enums.Role) gin.HandlerFunc { return } + if userInfo.Role < role { + c.Status(http.StatusForbidden) + } + var body T if err := c.ShouldBindJSON(&body); err != nil { - c.JSON(http.StatusBadRequest, err) + c.String(http.StatusBadRequest, err.Error()) // TODO: implement automatic validation here return diff --git a/backend/internal/models/auth.go b/backend/internal/models/auth.go index df54000..1d63a09 100644 --- a/backend/internal/models/auth.go +++ b/backend/internal/models/auth.go @@ -25,7 +25,7 @@ type Tokens struct { type RegistrationBeginRequest struct { Username string `json:"username" binding:"required,min=3,max=20"` Email *string `json:"email" binding:"email"` - Password string `json:"password" binding:"required,password"` // TODO: password checking + Password string `json:"password" binding:"required"` // TODO: password checking } // TODO: length check diff --git a/backend/internal/utils/request.go b/backend/internal/utils/request.go new file mode 100644 index 0000000..876c823 --- /dev/null +++ b/backend/internal/utils/request.go @@ -0,0 +1,35 @@ +// Copyright (c) 2025 Nikolai Papin +// +// This file is part of Easywish +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +// the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package utils + +import ( + "easywish/internal/middleware" + + "github.com/gin-gonic/gin" +) + +func GetRequest[T any](c *gin.Context) (*middleware.Request[T], bool) { + + req, ok := c.Get("request") + request := req.(middleware.Request[T]) + if !ok { + return nil, false + } + + return &request, true +}