package handlers import ( "log" "net/http" "slices" "strings" "gitea.tecamino.com/paadi/access-handler/models" "gitea.tecamino.com/paadi/tecamino-logger/logging" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" ) // SetMiddlewareLogger // // Description: // // Registers a Gin middleware that attaches a custom logger instance // to every incoming request via the Gin context. This allows other // middleware and handlers to access the logger using: // // logger := c.MustGet("logger").(*logging.Logger) // // Parameters: // - r: The Gin engine to which the middleware is applied. // - logger: A pointer to the application's custom logger. // // Usage: // // SetMiddlewareLogger(router, logger) func (aH *AccessHandler) SetMiddlewareLogger(r *gin.Engine) { // Add middleware that injects logger into context r.Use(func(c *gin.Context) { c.Set("logger", aH.logger) c.Next() }) } // AuthMiddleware // // Description: // // A Gin middleware that performs authentication using a JWT token stored // in a cookie named "access_token". It validates the token and extracts // the user's role from the claims, storing it in the Gin context. // // Behavior: // - Requires that SetMiddlewareLogger was used earlier to inject the logger. // - If the JWT cookie is missing or invalid, it aborts the request with // an appropriate HTTP error (401 or 500). // // Returns: // // A Gin handler function that can be used as middleware. // // Usage: // // r.Use(AuthMiddleware()) func (aH *AccessHandler) AuthMiddleware() gin.HandlerFunc { return func(c *gin.Context) { // Retrieve logger from Gin context middlewareLogger, ok := c.Get("logger") if !ok { log.Fatal("middleware logger not set — use SetMiddlewareLogger first") c.AbortWithStatusJSON(http.StatusInternalServerError, http.StatusInternalServerError) return } logger := middlewareLogger.(*logging.Logger) // Read access token from cookie cookie, err := c.Cookie("access_token") if err != nil { logger.Error("AuthMiddleware", err) c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"message": "not logged in"}) return } // Parse and validate JWT token token, err := jwt.Parse(cookie, func(t *jwt.Token) (any, error) { return ACCESS_SECRET, nil }) if err != nil || !token.Valid { logger.Error("AuthMiddleware", err) c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"message": "invalid token"}) return } // Extract custom claims (role) if claims, ok := token.Claims.(jwt.MapClaims); ok { role, _ := claims["role"].(string) c.Set("role", role) } c.Next() } } // (AccessHandler).AuthorizeRole // // Description: // // A role-based authorization middleware. It checks whether the authenticated // user (based on the "role" set in AuthMiddleware) has permission to access // the given route. The check is performed by comparing the requested URL // path against the user’s allowed permissions. // // Parameters: // - suffix: A URL prefix to trim from the request path before matching // permissions (e.g., "/api/v1"). // // Behavior: // - Fetches the user role from Gin context. // - Uses aH.GetRoleByKey() to retrieve role records and permissions. // - Grants access (calls c.Next()) if a matching permission is found. // - Denies access (401 Unauthorized) if no permission matches. // // Usage: // // router.GET("/secure/:id", aH.AuthorizeRole("/api/v1")) func (aH *AccessHandler) AuthorizeRole(suffix string, exeptions ...string) gin.HandlerFunc { return func(c *gin.Context) { aH.logger.Debug("AuthorizeRole", "permission path of url path") permissionPath := strings.TrimPrefix(c.Request.URL.Path, suffix+"/") aH.logger.Debug("AuthorizeRole", "get set role") role, ok := c.Get("role") if !ok { aH.logger.Error("AuthorizeRole", "no role set") c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": http.StatusInternalServerError}) return } // Fetch roles and associated permissions from the database or store var roles []models.Role err := aH.dbHandler.GetByKey(&roles, "role", role, false) if err != nil { aH.logger.Error("AuthorizeRole", err) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": http.StatusInternalServerError}) return } // Validate that a role was found if len(roles) == 0 { aH.logger.Error("AuthorizeRole", "not logged in") c.JSON(http.StatusUnauthorized, http.StatusUnauthorized) return } else if len(roles) > 1 { aH.logger.Error("AuthorizeRole", "more than one record found") c.JSON(http.StatusInternalServerError, http.StatusInternalServerError) return } // check exeptions if slices.Contains(exeptions, permissionPath) { c.Next() return } // Check permissions for _, permission := range roles[0].Permissions { if permission.Name == permissionPath { c.Next() return } } // Access denied aH.logger.Error("AuthorizeRole", "Forbidden") c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"message": "Forbidden"}) } }