Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Go RBAC Authorization using Gin, GORM, and Casbin

Tech 1

Casbin Model Configuration

The RBAC model is defined in access_control_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) && r.obj == p.obj && r.act == p.act
  • r = sub, obj, act: Specifies the request parameters: subject (user or role), object (target resource or URL), and action (HTTP method or operation type).
  • p = sub, obj, act: Defines the policy structure. For instance, editor, /documents, POST permits the editor role to perform POST requests on the /documents resource.
  • e = some(where (p.eft == allow)): The policy effect dictates that access is granted if any matched policy results in an allow decision.
  • g = _, _: Establishes the role hierarchy structure. For example, david, editor designates david as an editor.
  • m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act: The matcher algorithm validates the request by resolving the subject's role, confirming the target object, and verifying the requested action.

Policy Storage

Authorization rules can be persisted in a CSV file or a relational database. A sample CSV format:

p, viewer, /documents, GET
p, editor, /documents, GET
p, editor, /documents, POST
g, emma, viewer
g, david, editor

When using a database, the schema and initial data resemble the following:

CREATE TABLE authorization_rule (
    ptype VARCHAR(100),
    v0 VARCHAR(100),
    v1 VARCHAR(100),
    v2 VARCHAR(100)
);
INSERT INTO authorization_rule VALUES('p', 'viewer', '/documents', 'GET');
INSERT INTO authorization_rule VALUES('p', 'editor', '/documents', 'GET');
INSERT INTO authorization_rule VALUES('p', 'editor', '/documents', 'POST');
INSERT INTO authorization_rule(ptype, v0, v1) VALUES('g', 'emma', 'viewer');
INSERT INTO authorization_rule(ptype, v0, v1) VALUES('g', 'david', 'editor');

Gin Middleware Implementation

Required dependencies:

github.com/casbin/gorm-adapter/v3
gorm.io/gorm
github.com/casbin/casbin/v2

Approach 1: Manually iterating through the user's assigned roles:

func ValidateRoleAccess(dbAdapter *gormadapter.Adapter) gin.HandlerFunc {
    return func(ctx *gin.Context) {
        targetPath := ctx.Request.URL.Path
        httpVerb := ctx.Request.Method
        
        // Simulated roles assigned to the authenticated user
        userRoles := []string{"viewer"} 
        
        authEnforcer := casbin.NewEnforcer("config/access_control_model.conf", dbAdapter)
        
        var permitted bool
        var checkErr error
        
        for _, role := range userRoles {
            permitted, checkErr = authEnforcer.Enforce(role, targetPath, httpVerb)
            if permitted {
                break
            }
        }
        
        if checkErr != nil {
            ctx.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": checkErr.Error()})
            return
        }
        
        if !permitted {
            ctx.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "access denied"})
            return
        }
        
        ctx.Next()
    }
}

Approach 2: Direct user validation where Casbin automatically resolves role inheritance from the policy:

func ValidateUserAccess(dbAdapter *gormadapter.Adapter) gin.HandlerFunc {
    return func(ctx *gin.Context) {
        targetPath := ctx.Request.URL.Path
        httpVerb := ctx.Request.Method
        
        // Retrieve authenticated user identifier from context
        currentUser := "david" 
        
        authEnforcer := casbin.NewEnforcer("config/access_control_model.conf", dbAdapter)
        
        // Casbin automatically resolves roles assigned to currentUser via 'g' rules
        permitted, checkErr := authEnforcer.Enforce(currentUser, targetPath, httpVerb)
        
        if checkErr != nil {
            ctx.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": checkErr.Error()})
            return
        }
        
        if !permitted {
            ctx.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "access denied"})
            return
        }
        
        ctx.Next()
    }
}
Tags: go

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.