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.
Scanned 9/12/2026
Install to Claude Code
npx -y skills add aibot88/sec_skill_store --skill implementing-casbin --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Implementing Casbin?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/aibot88-implementing-casbin)More formats (shields.io, HTML) on the badges page.
---
name: implementing-casbin
description: 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.
version: 1.0.0
---
# 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
```bash
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`:
```ini
[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
```go
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
```go
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
```go
// 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)
```ini
[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:**
- `g(r.sub, p.sub)`: Role inheritance (user -> role)
- `keyMatch2`: Supports `/path/:id` patterns
### ABAC Model
```ini
[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:**
```go
// 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)
```ini
[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:**
```go
e.AddRoleForUserInDomain("alice", "admin", "tenant1")
e.AddPolicy("admin", "tenant1", "/api/*", "GET")
allowed, _ := e.Enforce("alice", "tenant1", "/api/users", "GET") // true
```
---
## GORM Adapter Setup
### PostgreSQL
```go
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
```go
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
```go
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(10)
sqlDB.SetMaxOpenConns(100)
sqlDB.SetConnMaxLifetime(time.Hour)
```
---
## Policy Management
### Adding Policies
```go
e.AddPolicy("alice", "/api/users", "GET") // Direct permission
e.AddRoleForUser("alice", "admin") // Assign role
e.AddRolesForUser("bob", []string{"viewer", "editor"}) // Multiple roles
```
### Removing Policies
```go
e.RemovePolicy("alice", "/api/users", "GET")
e.RemoveFilteredPolicy(0, "alice") // All policies for subject
e.DeleteRoleForUser("alice", "admin")
```
### Querying Policies
```go
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
```go
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
```go
e.EnableAutoSave(true) // Enable auto-save
// Or: e.SavePolicy() // Manual save
```
### keyMatch not working with path params
Use correct matcher function:
- `keyMatch2`: `/users/:id` patterns
- `keyMatch3`: `/users/{id}` patterns
### Slow performance with large policy sets
```go
// 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](https://casbin.org/docs/en/overview)
- [GORM Adapter](https://github.com/casbin/gorm-adapter)
- See **REFERENCE.md** for complete API reference and built-in functions
- See **TEMPLATES.md** for copy-paste middleware and test templates
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!