Claude-skill-registry implementing-casbin
Implement role-based (RBAC) and attribute-based (ABAC) access control in Go using Casbin. Covers model configuration, GORM adapters, Chi/gRPC middleware, and production patterns. Use when implementing authorization in Go services.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/implementing-casbin" ~/.claude/skills/majiayu000-claude-skill-registry-implementing-casbin && rm -rf "$T"
manifest:
skills/data/implementing-casbin/SKILL.mdsource content
Implementing Casbin Authorization
Purpose
Implement fine-grained authorization in Go services using Casbin's policy-based access control. Supports RBAC (role-based), ABAC (attribute-based), and hybrid models with database-backed policy storage.
When NOT to Use
- Simple API key authentication (use middleware directly)
- Single-user applications (no authorization needed)
- OAuth/OIDC token validation only (use dedicated auth libraries)
- Static, compile-time permissions (use Go interfaces/types)
Quick Start Workflow
Step 1: Install Dependencies
go get github.com/casbin/casbin/v2 go get github.com/casbin/gorm-adapter/v3 go get gorm.io/driver/postgres # or mysql
Step 2: Create Model File
Create
config/rbac_model.conf:
[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) && keyMatch2(r.obj, p.obj) && r.act == p.act
Step 3: Initialize Enforcer with GORM
package authz import ( "fmt" "github.com/casbin/casbin/v2" gormadapter "github.com/casbin/gorm-adapter/v3" "gorm.io/driver/postgres" "gorm.io/gorm" ) func NewEnforcer(dsn, modelPath string) (*casbin.Enforcer, error) { db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) if err != nil { return nil, fmt.Errorf("connect to database: %w", err) } adapter, err := gormadapter.NewAdapterByDB(db) if err != nil { return nil, fmt.Errorf("create adapter: %w", err) } e, err := casbin.NewEnforcer(modelPath, adapter) if err != nil { return nil, fmt.Errorf("create enforcer: %w", err) } e.EnableAutoSave(true) return e, nil }
Step 4: Add Chi Middleware
func Authorize(e *casbin.Enforcer) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { user, ok := r.Context().Value("userID").(string) if !ok || user == "" { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } allowed, err := e.Enforce(user, r.URL.Path, r.Method) if err != nil { http.Error(w, "Authorization error", http.StatusInternalServerError) return } if !allowed { http.Error(w, "Forbidden", http.StatusForbidden) return } next.ServeHTTP(w, r) }) } }
Step 5: Define Policies
// Add role e.AddRoleForUser("alice", "admin") // Add permissions for role e.AddPolicy("admin", "/api/users/*", "GET") e.AddPolicy("admin", "/api/users/*", "POST") // Check permission allowed, _ := e.Enforce("alice", "/api/users/123", "GET") // true
Model Configuration
RBAC Model (Recommended Default)
[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) && keyMatch2(r.obj, p.obj) && r.act == p.act
Key parts:
: Role inheritance (user -> role)g(r.sub, p.sub)
: SupportskeyMatch2
patterns/path/:id
ABAC Model
[request_definition] r = sub, obj, act [policy_definition] p = sub_rule, obj_rule, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = eval(p.sub_rule) && eval(p.obj_rule) && r.act == p.act
Example:
// User can only access their own documents e.AddPolicy("r.sub.ID == r.obj.OwnerID", "r.obj.Type == 'document'", "read")
RBAC with Domains (Multi-Tenant)
[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 && keyMatch2(r.obj, p.obj) && r.act == p.act
Example:
e.AddRoleForUserInDomain("alice", "admin", "tenant1") e.AddPolicy("admin", "tenant1", "/api/*", "GET") allowed, _ := e.Enforce("alice", "tenant1", "/api/users", "GET") // true
GORM Adapter Setup
PostgreSQL
dsn := "host=localhost user=app password=secret dbname=myapp port=5432 sslmode=disable" db, _ := gorm.Open(postgres.Open(dsn), &gorm.Config{}) adapter, _ := gormadapter.NewAdapterByDB(db)
MySQL
dsn := "user:password@tcp(localhost:3306)/myapp?charset=utf8mb4&parseTime=True" db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{}) adapter, _ := gormadapter.NewAdapterByDB(db)
Connection Pooling
sqlDB, _ := db.DB() sqlDB.SetMaxIdleConns(10) sqlDB.SetMaxOpenConns(100) sqlDB.SetConnMaxLifetime(time.Hour)
Policy Management
Adding Policies
e.AddPolicy("alice", "/api/users", "GET") // Direct permission e.AddRoleForUser("alice", "admin") // Assign role e.AddRolesForUser("bob", []string{"viewer", "editor"}) // Multiple roles
Removing Policies
e.RemovePolicy("alice", "/api/users", "GET") e.RemoveFilteredPolicy(0, "alice") // All policies for subject e.DeleteRoleForUser("alice", "admin")
Querying Policies
policies, _ := e.GetPolicy() // All policies roles, _ := e.GetRolesForUser("alice") // User's roles permissions, _ := e.GetImplicitPermissionsForUser("alice") // Including inherited hasRole, _ := e.HasRoleForUser("alice", "admin")
Testing
Unit Test Setup
func TestEnforcer(t *testing.T) { modelText := ` [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 ` m, _ := model.NewModelFromString(modelText) e, _ := casbin.NewEnforcer(m) e.AddPolicy("admin", "/users", "GET") e.AddRoleForUser("alice", "admin") allowed, _ := e.Enforce("alice", "/users", "GET") assert.True(t, allowed) }
See TEMPLATES.md for complete test templates.
Common Issues
Policies not persisting
e.EnableAutoSave(true) // Enable auto-save // Or: e.SavePolicy() // Manual save
keyMatch not working with path params
Use correct matcher function:
:keyMatch2
patterns/users/:id
:keyMatch3
patterns/users/{id}
Slow performance with large policy sets
// Load only relevant policies filter := gormadapter.Filter{P: []string{"", "tenant1"}} e.LoadFilteredPolicy(filter) // Or batch enforcement results, _ := e.BatchEnforce(requests)
Integration with Other Skills
- setup-go: Run before Casbin setup
- quality-check: Lint authorization code
- run-tests: Execute authorization tests
References
- Casbin Documentation
- GORM Adapter
- See REFERENCE.md for complete API reference and built-in functions
- See TEMPLATES.md for copy-paste middleware and test templates