Skip to main content

Overview

The rbac middleware provides role-based access control, allowing you to restrict routes based on user roles and permissions. Use it when you need:
  • Role-based route protection
  • Permission-based access control
  • Multi-level authorization

Installation

import "github.com/go-mizu/mizu/middlewares/rbac"

Quick Start

app := mizu.New()

// Get user role from context
getRoles := func(c *mizu.Ctx) []string {
    user := c.Get("user").(User)
    return user.Roles
}

// Require admin role
adminOnly := rbac.RequireRole(getRoles, "admin")

app.Get("/admin", adminHandler, adminOnly)

Configuration

Options

OptionTypeDescription
GetRolesfunc(*mizu.Ctx) []stringFunction to get user roles
GetPermissionsfunc(*mizu.Ctx) []stringFunction to get permissions
ErrorHandlerfunc(*mizu.Ctx) errorCustom 403 handler

Examples

Simple Role Check

getRoles := func(c *mizu.Ctx) []string {
    claims := jwt.GetClaims(c)
    return claims["roles"].([]string)
}

// Single role
app.Get("/admin", handler, rbac.RequireRole(getRoles, "admin"))

// Any of multiple roles
app.Get("/dashboard", handler, rbac.RequireAnyRole(getRoles, "admin", "manager"))

// All roles required
app.Get("/super", handler, rbac.RequireAllRoles(getRoles, "admin", "superuser"))

Permission-Based

getPerms := func(c *mizu.Ctx) []string {
    return getUserPermissions(c)
}

// Single permission
app.Post("/users", createUser, rbac.RequirePermission(getPerms, "users:create"))

// Multiple permissions
app.Delete("/users/:id", deleteUser, rbac.RequireAllPermissions(getPerms, "users:delete", "admin:access"))

Hierarchical Roles

hierarchy := rbac.NewHierarchy()
hierarchy.Add("superadmin", "admin", "user")
hierarchy.Add("admin", "moderator", "user")
hierarchy.Add("moderator", "user")

// superadmin and admin can access
app.Get("/manage", handler, rbac.RequireRoleWithHierarchy(getRoles, hierarchy, "admin"))

Custom Error Handler

app.Use(rbac.WithOptions(rbac.Options{
    GetRoles: getRoles,
    ErrorHandler: func(c *mizu.Ctx) error {
        return c.JSON(403, map[string]string{
            "error": "You don't have permission to access this resource",
        })
    },
}))

Route Groups

// Admin routes
admin := app.Group("/admin")
admin.Use(rbac.RequireRole(getRoles, "admin"))
admin.Get("/users", listUsers)
admin.Post("/users", createUser)

// Manager routes
manager := app.Group("/manager")
manager.Use(rbac.RequireAnyRole(getRoles, "admin", "manager"))
manager.Get("/reports", viewReports)

API Reference

Functions

// RequireRole requires a specific role
func RequireRole(getRoles func(*mizu.Ctx) []string, role string) mizu.Middleware

// RequireAnyRole requires any of the specified roles
func RequireAnyRole(getRoles func(*mizu.Ctx) []string, roles ...string) mizu.Middleware

// RequireAllRoles requires all specified roles
func RequireAllRoles(getRoles func(*mizu.Ctx) []string, roles ...string) mizu.Middleware

// RequirePermission requires a specific permission
func RequirePermission(getPerms func(*mizu.Ctx) []string, perm string) mizu.Middleware

// RequireAllPermissions requires all permissions
func RequireAllPermissions(getPerms func(*mizu.Ctx) []string, perms ...string) mizu.Middleware

// WithOptions creates middleware with configuration
func WithOptions(opts Options) mizu.Middleware

Hierarchy

// NewHierarchy creates role hierarchy
func NewHierarchy() *Hierarchy

// Add defines parent-child relationships
func (h *Hierarchy) Add(parent string, children ...string)

// RequireRoleWithHierarchy checks against hierarchy
func RequireRoleWithHierarchy(getRoles func(*mizu.Ctx) []string, h *Hierarchy, role string) mizu.Middleware

Technical Details

Implementation Overview

The RBAC middleware is built around a User struct that contains roles and permissions:
type User struct {
    ID          string
    Roles       []string
    Permissions []string
}

Context Management

User information is stored in the request context using a private context key:
  • Set(c *mizu.Ctx, user *User) - Stores user in context
  • Get(c *mizu.Ctx) *User - Retrieves user from context
  • Returns nil if no user is found in context

Role and Permission Checking

The middleware provides several helper functions for checking access:
  • HasRole(c *mizu.Ctx, role string) bool - Checks if user has a specific role
  • HasAnyRole(c *mizu.Ctx, roles ...string) bool - Checks if user has any of the specified roles
  • HasAllRoles(c *mizu.Ctx, roles ...string) bool - Checks if user has all specified roles
  • HasPermission(c *mizu.Ctx, permission string) bool - Checks if user has a specific permission

Middleware Functions

Role-based middlewares:
  • RequireRole(role string) - Requires a specific role
  • RequireAnyRole(roles ...string) - Requires any of the specified roles (OR logic)
  • RequireAllRoles(roles ...string) - Requires all specified roles (AND logic)
Permission-based middlewares:
  • RequirePermission(permission string) - Requires a specific permission
  • RequireAnyPermission(permissions ...string) - Requires any of the permissions
  • RequireAllPermissions(permissions ...string) - Requires all permissions
Convenience middlewares:
  • Admin() - Shorthand for RequireRole(“admin”)
  • Authenticated() - Checks if any user is present in context

Error Handling

  • Default behavior returns HTTP 403 Forbidden with “Access denied” text
  • Returns HTTP 401 Unauthorized with “Authentication required” for unauthenticated users
  • Custom error handlers can be wrapped using WithErrorHandler

Best Practices

  • Use JWT claims or session for role storage
  • Implement role hierarchy for complex permissions
  • Cache permission checks for performance
  • Log authorization failures for security monitoring

Testing

Test CaseDescriptionExpected Behavior
TestRequireRoleUser with required role accessing protected routeReturns 200 OK, access granted
TestRequireRole_ForbiddenUser without required role accessing protected routeReturns 403 Forbidden, access denied
TestRequireAnyRoleUser with one of multiple required rolesReturns 200 OK, access granted when user has editor role
TestRequireAllRoles (has all)User with all required rolesReturns 200 OK, access granted when user has both admin and editor roles
TestRequireAllRoles (missing one)User missing one of required rolesReturns 403 Forbidden, access denied when user has admin but not editor
TestRequirePermissionUser with required permission accessing resourceReturns 200 OK, access granted with write:posts permission
TestAdminUser with admin role accessing admin routeReturns 200 OK, access granted
TestAuthenticated (authenticated)Authenticated user accessing protected routeReturns 200 OK, access granted
TestAuthenticated (unauthenticated)Unauthenticated user accessing protected routeReturns 401 Unauthorized, authentication required
TestHasRoleChecking if user has specific rolesReturns true for user role, false for admin role
TestGetRetrieving user from contextReturns user object with correct ID
TestNoUserChecking role without user in contextReturns false, no access without user