Casbin

authentication-libraryaccess-controlRBACABACACLauthorizationsecuritypolicy-management

Authentication Library

Casbin

Overview

Casbin is a multi-language authorization library that supports various access control models such as ACL, RBAC, and ABAC. With flexible policy definition and model configuration, it can handle complex permission management requirements. It supports over 20 languages including Go, Python, Java, Node.js, and Rust, and is widely adopted as an authorization system for web applications, microservices, and cloud services. As of 2025, it continues active development.

Details

Casbin is an authorization library that can handle various access control models in a unified manner. It features the following key characteristics:

  • Multiple Access Control Models: Support for ACL, RBAC, ABAC, RESTful, deny/allow priority, and more
  • Flexible Policy Management: Policy storage in CSV, databases, and cloud storage
  • Model Configuration: Definition of access control rules and matchers in INI format
  • Multi-Language Support: Implementation in over 20 programming languages
  • Web Framework Integration: Integration with Express, Flask, Django, Spring Boot, and more
  • Distributed System Support: Authorization management in microservices and cloud environments

Pros and Cons

Pros

  • Flexibility to handle multiple access control models in a unified manner
  • Consistent authorization system across multi-language environments with rich language support
  • Clear policy management through model-driven design
  • Rich integration options with web frameworks
  • Proven track record and reliability in large-scale systems
  • Active community and continuous development

Cons

  • High initial learning cost and complex configuration in some cases
  • Understanding of Casbin concepts required for advanced features
  • May be over-featured for small-scale applications
  • Optimization required when performance is critical
  • Poor model design can make operation difficult
  • Debugging and troubleshooting can be complex

Reference Pages

Code Examples

Basic ACL Model Configuration

# model.conf - ACL Model Definition
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
# policy.csv - ACL Policy
p, alice, data1, read
p, bob, data2, write
p, charlie, data1, read
p, charlie, data1, write

Using Casbin in Go

package main

import (
    "fmt"
    "github.com/casbin/casbin/v2"
)

func main() {
    // Initialize Enforcer
    e, err := casbin.NewEnforcer("model.conf", "policy.csv")
    if err != nil {
        panic(err)
    }

    // Permission check
    res, err := e.Enforce("alice", "data1", "read")
    if err != nil {
        panic(err)
    }
    fmt.Printf("Alice can read data1: %t\n", res) // true

    res, err = e.Enforce("alice", "data2", "read")
    if err != nil {
        panic(err)
    }
    fmt.Printf("Alice can read data2: %t\n", res) // false

    // Add policy dynamically
    e.AddPolicy("alice", "data2", "read")
    
    res, err = e.Enforce("alice", "data2", "read")
    if err != nil {
        panic(err)
    }
    fmt.Printf("Alice can read data2 after adding policy: %t\n", res) // true

    // Remove policy
    e.RemovePolicy("alice", "data2", "read")
}

RBAC Model Implementation

# rbac_model.conf - RBAC Model Definition
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
# rbac_policy.csv - RBAC Policy
p, admin, data1, read
p, admin, data1, write
p, admin, data2, read
p, admin, data2, write
p, user, data1, read

g, alice, admin
g, bob, user
g, charlie, user
package main

import (
    "fmt"
    "github.com/casbin/casbin/v2"
)

func main() {
    // Initialize Enforcer with RBAC model
    e, err := casbin.NewEnforcer("rbac_model.conf", "rbac_policy.csv")
    if err != nil {
        panic(err)
    }

    // Role-based permission check
    res, _ := e.Enforce("alice", "data1", "write")
    fmt.Printf("Alice (admin) can write data1: %t\n", res) // true

    res, _ = e.Enforce("bob", "data1", "write")
    fmt.Printf("Bob (user) can write data1: %t\n", res) // false

    // Role management API
    roles, _ := e.GetRolesForUser("alice")
    fmt.Printf("Alice's roles: %v\n", roles) // [admin]

    users, _ := e.GetUsersForRole("admin")
    fmt.Printf("Admin users: %v\n", users) // [alice]

    // Dynamic role assignment
    e.AddRoleForUser("charlie", "admin")
    
    res, _ = e.Enforce("charlie", "data2", "write")
    fmt.Printf("Charlie (now admin) can write data2: %t\n", res) // true
}

ABAC Model with Attribute-Based Access Control

# abac_model.conf - ABAC Model Definition
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub.Department == r.obj.Owner
package main

import (
    "fmt"
    "github.com/casbin/casbin/v2"
)

type Subject struct {
    Name       string
    Department string
}

type Object struct {
    Name  string
    Owner string
}

func main() {
    // Initialize Enforcer with ABAC model
    e, err := casbin.NewEnforcer("abac_model.conf")
    if err != nil {
        panic(err)
    }

    // Attribute-based permission check
    sub := Subject{Name: "alice", Department: "engineering"}
    obj := Object{Name: "project1", Owner: "engineering"}

    res, _ := e.Enforce(sub, obj, "read")
    fmt.Printf("Alice (engineering) can access engineering project: %t\n", res) // true

    obj2 := Object{Name: "project2", Owner: "marketing"}
    res, _ = e.Enforce(sub, obj2, "read")
    fmt.Printf("Alice (engineering) can access marketing project: %t\n", res) // false
}

Middleware Integration in Web Applications

package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
    "github.com/casbin/casbin/v2"
)

func NewCasbinMiddleware(enforcer *casbin.Enforcer) gin.HandlerFunc {
    return func(c *gin.Context) {
        // Get user information (from JWT token or session)
        user := getUserFromToken(c.GetHeader("Authorization"))
        
        // Identify resource and action
        resource := c.Request.URL.Path
        action := c.Request.Method

        // Permission check
        allowed, err := enforcer.Enforce(user, resource, action)
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": "Authorization check failed"})
            c.Abort()
            return
        }

        if !allowed {
            c.JSON(http.StatusForbidden, gin.H{"error": "Access denied"})
            c.Abort()
            return
        }

        c.Next()
    }
}

func main() {
    // Initialize Casbin
    enforcer, _ := casbin.NewEnforcer("model.conf", "policy.csv")

    r := gin.Default()
    
    // Apply authorization middleware
    authorized := r.Group("/api", NewCasbinMiddleware(enforcer))
    {
        authorized.GET("/users", getUsers)
        authorized.POST("/users", createUser)
        authorized.DELETE("/users/:id", deleteUser)
    }

    r.Run(":8080")
}

func getUserFromToken(token string) string {
    // Extract user information from JWT token
    // Implementation details omitted
    return "alice"
}

func getUsers(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"users": []string{"alice", "bob"}})
}

func createUser(c *gin.Context) {
    c.JSON(http.StatusCreated, gin.H{"message": "User created"})
}

func deleteUser(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"message": "User deleted"})
}

RBAC Model with Domain/Tenant Separation

# rbac_with_domains_model.conf
[request_definition]
r = sub, dom, obj, act

[policy_definition]
p = sub, dom, obj, act

[role_definition]
g = _, _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && r.obj == p.obj && r.act == p.act
# rbac_with_domains_policy.csv
p, admin, tenant1, data1, read
p, admin, tenant1, data1, write
p, admin, tenant2, data2, read
p, user, tenant1, data1, read

g, alice, admin, tenant1
g, bob, user, tenant1
g, charlie, admin, tenant2
package main

import (
    "fmt"
    "github.com/casbin/casbin/v2"
)

func main() {
    // Initialize Enforcer with domain-enabled RBAC model
    e, err := casbin.NewEnforcer("rbac_with_domains_model.conf", "rbac_with_domains_policy.csv")
    if err != nil {
        panic(err)
    }

    // Domain-specific permission check
    res, _ := e.Enforce("alice", "tenant1", "data1", "write")
    fmt.Printf("Alice can write data1 in tenant1: %t\n", res) // true

    res, _ = e.Enforce("alice", "tenant2", "data2", "read")
    fmt.Printf("Alice can read data2 in tenant2: %t\n", res) // false

    // Domain-specific role management
    roles, _ := e.GetRolesForUserInDomain("alice", "tenant1")
    fmt.Printf("Alice's roles in tenant1: %v\n", roles) // [admin]

    // Verify cross-tenant access prevention
    res, _ = e.Enforce("bob", "tenant2", "data2", "read")
    fmt.Printf("Bob (tenant1 user) can read tenant2 data: %t\n", res) // false
}

Policy Management with Database

package main

import (
    "github.com/casbin/casbin/v2"
    gormadapter "github.com/casbin/gorm-adapter/v3"
    "gorm.io/driver/postgres"
    "gorm.io/gorm"
)

func main() {
    // Database connection
    db, err := gorm.Open(postgres.Open("postgres://user:password@localhost/casbin_db"), &gorm.Config{})
    if err != nil {
        panic(err)
    }

    // Use Gorm Adapter
    adapter, err := gormadapter.NewAdapterByDB(db)
    if err != nil {
        panic(err)
    }

    // Initialize Enforcer with database adapter
    e, err := casbin.NewEnforcer("model.conf", adapter)
    if err != nil {
        panic(err)
    }

    // Load policies from database
    e.LoadPolicy()

    // Dynamic policy management (persisted to database)
    e.AddPolicy("alice", "data1", "read")
    e.AddPolicy("bob", "data2", "write")
    
    // Save policies
    e.SavePolicy()

    // Permission check
    res, _ := e.Enforce("alice", "data1", "read")
    fmt.Printf("Alice can read data1: %t\n", res) // true
}