package middleware // Reference: P2E §2.7 — RoleCheck middleware // Simple middleware that verifies the user has the specified role in the current account. // Used for endpoints that require a specific role regardless of permission dimensions. import ( "net/http" "github.com/gin-gonic/gin" "github.com/gochat/gochat/internal/auth" "github.com/gochat/gochat/pkg/response" ) // RoleCheck creates a middleware that verifies the current user has the specified role. // This is a simple role gate — for more granular permission checks, use PolicyMiddleware. // // Usage: // router.POST("/accounts/:id/users", RoleCheck("administrator"), inviteUser) // router.GET("/reports", RoleCheck("administrator"), viewReports) // // Valid role values: "agent", "administrator", "custom_role" func RoleCheck(role string) gin.HandlerFunc { return func(c *gin.Context) { pc, exists := c.Get("policy_context") if !exists { response.AbortWithStatusError(c, http.StatusForbidden, response.ErrForbidden, "Policy context not found — ensure AccountScope middleware is applied first") return } policyCtx, ok := pc.(*auth.PolicyContext) if !ok { response.AbortWithStatusError(c, http.StatusForbidden, response.ErrForbidden, "Invalid policy context type") return } switch role { case "administrator": if !policyCtx.IsAdministrator() { response.AbortWithStatusError(c, http.StatusForbidden, response.ErrForbidden, "Administrator role required") return } case "agent": if !policyCtx.IsAgent() { response.AbortWithStatusError(c, http.StatusForbidden, response.ErrForbidden, "Agent role required") return } case "custom_role": if !policyCtx.IsCustomRole() { response.AbortWithStatusError(c, http.StatusForbidden, response.ErrForbidden, "Custom role required") return } default: // Check if the user's role matches exactly if policyCtx.Role != role { response.AbortWithStatusError(c, http.StatusForbidden, response.ErrForbidden, "Role '"+role+"' required, but user has role '"+policyCtx.Role+"'") return } } c.Next() } } // RoleCheckAny creates a middleware that verifies the current user has ANY of the specified roles. // Useful for endpoints accessible to multiple roles. // // Usage: // router.GET("/dashboard", RoleCheckAny("administrator", "custom_role"), viewDashboard) func RoleCheckAny(roles ...string) gin.HandlerFunc { roleSet := make(map[string]bool, len(roles)) for _, r := range roles { roleSet[r] = true } return func(c *gin.Context) { pc, exists := c.Get("policy_context") if !exists { response.AbortWithStatusError(c, http.StatusForbidden, response.ErrForbidden, "Policy context not found — ensure AccountScope middleware is applied first") return } policyCtx, ok := pc.(*auth.PolicyContext) if !ok { response.AbortWithStatusError(c, http.StatusForbidden, response.ErrForbidden, "Invalid policy context type") return } if !roleSet[policyCtx.Role] { response.AbortWithStatusError(c, http.StatusForbidden, response.ErrForbidden, "One of the following roles required: "+formatRoles(roles)) return } c.Next() } } func formatRoles(roles []string) string { result := "" for i, r := range roles { if i > 0 { result += ", " } result += r } return result }