package handlers import ( "authorization/helper" "authorization/middleware" "authorization/models" "authorization/services" "encoding/json" "log" "net/http" ) var authService *models.CachedAuthorizationService // InitAuthService initializes the authorization service with caching func InitAuthService() { authService = services.NewCachedAuthorizationService() } // AuthorizeHandler godoc // @Summary Check user authorization (RBAC + ABAC) // @Description Validates if a user has permission to perform an action on a resource using Role-Based and Attribute-Based Access Control // @Tags authorization // @Accept json // @Produce json // @Param request body models.AuthorizationContext true "Authorization context with resource data" // @Success 200 {object} models.AuthorizationResult // @Failure 400 {object} map[string]string // @Failure 401 {object} map[string]string // @Failure 403 {object} models.AuthorizationResult // @Security BearerToken // @Router /v1/auth/check [post] func AuthorizeHandler(w http.ResponseWriter, r *http.Request) { // Get claims from JWT middleware claims, ok := middleware.GetClaims(r) if !ok { helper.RespondWithError(w, http.StatusUnauthorized, "Unauthorized") return } var ctx models.AuthorizationContext err := json.NewDecoder(r.Body).Decode(&ctx) if err != nil { helper.RespondWithError(w, http.StatusBadRequest, "Invalid request payload") return } // Validate request if ctx.UserID == "" || ctx.Resource == "" || ctx.Action == "" { helper.RespondWithError(w, http.StatusBadRequest, "Missing required fields: user_id, resource, action") return } log.Print("Authorization request for user=", ctx.UserID, ", resource=", ctx.Resource, ", action=", ctx.Action) log.Print("JWT claims user=", claims.UserID, ", role=", claims.RoleID) // Verify JWT user matches request user (security check) if ctx.UserID != claims.UserID { helper.RespondWithError(w, http.StatusForbidden, "User ID mismatch") return } // Initialize maps if nil if ctx.ResourceData == nil { ctx.ResourceData = make(map[string]string) } if ctx.Environment == nil { ctx.Environment = make(map[string]string) } if ctx.RoleID != claims.RoleID { helper.RespondWithError(w, http.StatusForbidden, "Role ID mismatch") return } // Perform authorization log.Printf("[Handler] Performing authorization check for user=%s, resource=%s, action=%s", ctx.UserID, ctx.Resource, ctx.Action) result, err := services.AuthorizeWithCache(authService, &ctx) if err != nil { helper.LogError(err, "Authorization service error") log.Printf("✗ Authorization service error for user=%s: %v", ctx.UserID, err) helper.RespondWithError(w, http.StatusInternalServerError, "Authorization check failed") return } // Return result if result.Allowed { log.Printf("✓ [Handler] Authorization ALLOWED - Returning 200 OK to client") // Return response matching AuthorizationResponse model for client compatibility response := map[string]interface{}{ "allowed": result.Allowed, "reason": result.Message, } helper.RespondWithJSON(w, http.StatusOK, response) } else { log.Printf("✗ [Handler] Authorization DENIED - Returning 403 Forbidden to client (reason: %s)", result.Message) // Return response matching AuthorizationResponse model for client compatibility response := map[string]interface{}{ "allowed": result.Allowed, "reason": result.Message, } helper.RespondWithJSON(w, http.StatusForbidden, response) } }