Files
gochat/internal/middleware/role_check.go
T
2026-06-04 15:44:48 +08:00

117 lines
3.3 KiB
Go

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
}