Skip to content

feat: add support for Casbin v3 with new middleware and configuration options#1930

Open
PedroDBFlores wants to merge 4 commits into
gofiber:mainfrom
PedroDBFlores:feat/casbin-v3-support
Open

feat: add support for Casbin v3 with new middleware and configuration options#1930
PedroDBFlores wants to merge 4 commits into
gofiber:mainfrom
PedroDBFlores:feat/casbin-v3-support

Conversation

@PedroDBFlores
Copy link
Copy Markdown

@PedroDBFlores PedroDBFlores commented May 19, 2026

This pull request adds support for Casbin v3 to the Fiber Casbin middleware, allowing users to choose between Casbin v2 and v3 enforcers. The main changes include the introduction of a new configuration struct and constructor for v3 and internal refactoring to generalize the middleware logic.

Casbin v3 Support:

  • Added a new ConfigV3 struct in config_v3.go to support Casbin v3 enforcers, including default values and a helper function configDefaultV3 to initialize the configuration.
  • Introduced the NewV3 constructor in casbin.go to create middleware instances using Casbin v3 enforcers.

Middleware Refactoring:

  • Refactored the Middleware struct to store handler functions and enforcer methods directly, abstracting over v2 and v3 enforcer differences.

Documentation and Dependency Updates:

  • Updated documentation in config.go to clarify that Enforcer refers specifically to Casbin v2, and added Casbin v3 as a dependency in go.mod.

Summary by CodeRabbit

  • New Features

    • Added support for Casbin v3 authorization with a new v3 configuration path and default v3 config values.
  • Refactor

    • Middleware wiring updated to use injected handlers for subject lookup, enforcement, and role retrieval (preserves existing authorization behavior).
  • Tests

    • Added comprehensive v3 tests covering permissions, roles, and route-based authorization.

Review Change Stack

@PedroDBFlores PedroDBFlores requested a review from a team as a code owner May 19, 2026 12:57
@PedroDBFlores PedroDBFlores requested review from ReneWerner87, Copilot, efectn, gaby and sixcolors and removed request for a team May 19, 2026 12:57
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces Casbin v3 support by adding the NewV3 constructor and ConfigV3 structure, while refactoring the Middleware struct to store specific function pointers for enforcement and role lookup. Review feedback identified critical nil pointer dereference risks in both New and NewV3 when using default configurations, as the initialization logic currently bypasses enforcer setup. Additionally, a correction was requested to mark the Casbin v3 dependency as a direct requirement in the go.mod file.

Comment thread v3/casbin/config_v3.go
Comment on lines +48 to +52
if len(config) < 1 {
return ConfigV3Default, nil
}

cfg := config[0]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The configDefaultV3 function returns early when no configuration is provided, which skips the initialization of the Casbin enforcer and other default values. This will lead to a nil pointer dereference in NewV3 when it attempts to capture the enforcer's methods.

Suggested change
if len(config) < 1 {
return ConfigV3Default, nil
}
cfg := config[0]
cfg := ConfigV3Default
if len(config) > 0 {
cfg = config[0]
}

Comment thread v3/casbin/casbin.go
Comment on lines +33 to +34
enforce: cfg.Enforcer.Enforce,
getRolesForUser: cfg.Enforcer.GetRolesForUser,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Accessing cfg.Enforcer.Enforce here will cause a nil pointer dereference if New() is called without arguments. This is because configDefault (in config.go) returns early and does not initialize the enforcer when no config is passed. While this issue existed in the previous version, the refactoring now causes a panic at startup instead of when the middleware is first used. Please ensure configDefault is also updated to handle the default case correctly.

Comment thread v3/casbin/go.mod Outdated
require (
github.com/andybalholm/brotli v1.2.1 // indirect
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
github.com/casbin/casbin/v3 v3.10.0 // indirect
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

github.com/casbin/casbin/v3 is a direct dependency of this package (imported in config_v3.go) and should not be marked as // indirect.

Suggested change
github.com/casbin/casbin/v3 v3.10.0 // indirect
github.com/casbin/casbin/v3 v3.10.0

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Casbin v3 support to the v3/casbin Fiber middleware by introducing a v3-specific configuration/constructor and refactoring middleware internals to work with either v2 or v3 enforcers via function pointers.

Changes:

  • Added ConfigV3 + defaulting helper (configDefaultV3) for Casbin v3 configuration.
  • Added NewV3 constructor and refactored Middleware to store resolved handler/enforcer function pointers (v2 or v3).
  • Added Casbin v3 dependency entries and a new v3-focused test suite.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
v3/casbin/go.sum Adds checksums for Casbin v3 dependency.
v3/casbin/go.mod Adds Casbin v3 module requirement.
v3/casbin/config.go Clarifies that Config.Enforcer refers to Casbin v2 (comment update).
v3/casbin/config_v3.go Introduces v3 configuration struct, defaults, and defaulting logic.
v3/casbin/casbin.go Refactors middleware to version-agnostic function pointers and adds NewV3.
v3/casbin/casbin_v3_test.go Adds tests covering middleware behavior when using a Casbin v3 enforcer.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread v3/casbin/config_v3.go
Comment on lines +48 to +53
if len(config) < 1 {
return ConfigV3Default, nil
}

cfg := config[0]

Comment thread v3/casbin/config.go
Comment on lines +20 to 22
// Enforcer is a Casbin v2 enforcer. If you want to use your own enforcer.
// Optional. Default: nil (one is created from ModelFilePath and PolicyAdapter).
Enforcer *casbin.Enforcer
Comment thread v3/casbin/go.mod
Comment on lines 10 to 14
require (
github.com/andybalholm/brotli v1.2.1 // indirect
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
github.com/casbin/casbin/v3 v3.10.0 // indirect
github.com/casbin/govaluate v1.10.0 // indirect
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 2026

📝 Walkthrough

Walkthrough

This PR refactors the Casbin middleware to use dependency injection and adds support for Casbin v3. The Middleware struct now stores injected function handlers for authorization logic instead of a config reference, allowing both v2 (via New) and v3 (via NewV3) configurations. A new ConfigV3 struct with enforcer-creation logic enables v3 setup, and comprehensive tests validate all authorization methods with v3.

Changes

Casbin v3 Middleware Support with Dependency Injection

Layer / File(s) Summary
v3 Configuration and Enforcer Wiring
v3/casbin/config_v3.go
ConfigV3 struct defines model/policy paths, optional pre-built enforcer, subject lookup, and response handlers. configDefaultV3 applies defaults and constructs a v3 enforcer when not supplied.
Middleware Refactoring to Dependency Injection
v3/casbin/casbin.go, v3/casbin/config.go
Middleware now stores injected function pointers (lookup, enforce, unauthorized, forbidden, getRolesForUser) instead of a config reference. NewV3 constructor wires these from v3 configuration; New continues to wire from v2 configuration. Config.Enforcer comment clarified as v2-specific.
Authorization Methods Using Injected Functions
v3/casbin/casbin.go
RequiresPermissions, RoutePermission, and RequiresRoles updated to use injected middleware functions instead of m.config.*. Authorization flow, validation rules, and HTTP status handling remain unchanged.
Casbin v3 Test Coverage
v3/casbin/casbin_v3_test.go
Mock adapter and test helper support v3 enforcer setup. Test_RequiresPermission_V3, Test_RequiresRoles_V3, and Test_RoutePermission_V3 validate all three authorization methods with v3, covering success, failure, and edge cases.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • #1929: Directly related—both introduce Casbin v3 support via NewV3 constructor and v3-specific configuration/enforcer wiring.

Poem

🐰 I hopped into code with a curious nose,

Wired handlers neatly in tidy rows,
V3 enforcers hum, policies align,
Tests scurry through every role and line,
A carrot of middleware, clean and fine.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding Casbin v3 support to the middleware with new configuration options. It is concise, specific, and clearly communicates the primary purpose of the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@v3/casbin/config_v3.go`:
- Around line 47-50: configDefaultV3 currently returns ConfigV3Default with a
nil Enforcer when no args are passed; update configDefaultV3 to construct and
assign a working default enforcer to ConfigV3Default.Enforcer before returning.
Locate configDefaultV3 and ensure you instantiate the default enforcer (using
the project's enforcer constructor/helper, e.g. NewEnforcer or NewEnforcerV3)
and set ConfigV3Default.Enforcer = <constructed enforcer>, handling any
construction error and returning it instead of nil so callers always receive a
ConfigV3 with a non-nil Enforcer.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4c3e8534-0734-4d75-8429-91ba2c7d7704

📥 Commits

Reviewing files that changed from the base of the PR and between 7187a10 and 8ee8380.

⛔ Files ignored due to path filters (2)
  • v3/casbin/go.mod is excluded by !**/*.mod
  • v3/casbin/go.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (4)
  • v3/casbin/casbin.go
  • v3/casbin/casbin_v3_test.go
  • v3/casbin/config.go
  • v3/casbin/config_v3.go
📜 Review details
🔇 Additional comments (5)
v3/casbin/casbin_v3_test.go (1)

1-404: LGTM!

v3/casbin/casbin.go (2)

45-50: Downstream effect of nil-enforcer default path.

This wiring issue is covered by the root-cause comment in configDefaultV3; fixing that resolves this path as well.


9-37: LGTM!

Also applies to: 54-156

v3/casbin/config_v3.go (1)

1-46: LGTM!

Also applies to: 52-84

v3/casbin/config.go (1)

20-21: LGTM!

Comment thread v3/casbin/config_v3.go
Comment on lines +47 to +50
func configDefaultV3(config ...ConfigV3) (ConfigV3, error) {
if len(config) < 1 {
return ConfigV3Default, nil
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Create the default v3 enforcer in the no-arg path.

When no config is passed, this returns ConfigV3Default as-is, which leaves Enforcer nil. That contradicts the documented default behavior and can cause nil-enforcer failures later in middleware execution.

💡 Proposed fix
 func configDefaultV3(config ...ConfigV3) (ConfigV3, error) {
-	if len(config) < 1 {
-		return ConfigV3Default, nil
-	}
-
-	cfg := config[0]
+	cfg := ConfigV3Default
+	if len(config) > 0 {
+		cfg = config[0]
+	}
 
 	if cfg.Enforcer == nil {
 		if cfg.ModelFilePath == "" {
 			cfg.ModelFilePath = ConfigV3Default.ModelFilePath
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func configDefaultV3(config ...ConfigV3) (ConfigV3, error) {
if len(config) < 1 {
return ConfigV3Default, nil
}
func configDefaultV3(config ...ConfigV3) (ConfigV3, error) {
cfg := ConfigV3Default
if len(config) > 0 {
cfg = config[0]
}
if cfg.Enforcer == nil {
if cfg.ModelFilePath == "" {
cfg.ModelFilePath = ConfigV3Default.ModelFilePath
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/casbin/config_v3.go` around lines 47 - 50, configDefaultV3 currently
returns ConfigV3Default with a nil Enforcer when no args are passed; update
configDefaultV3 to construct and assign a working default enforcer to
ConfigV3Default.Enforcer before returning. Locate configDefaultV3 and ensure you
instantiate the default enforcer (using the project's enforcer
constructor/helper, e.g. NewEnforcer or NewEnforcerV3) and set
ConfigV3Default.Enforcer = <constructed enforcer>, handling any construction
error and returning it instead of nil so callers always receive a ConfigV3 with
a non-nil Enforcer.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept the logic the same as the v2 config, in order to maintain coherence between implementations

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
cat v3/casbin/config.go

Length of output: 2480


@PedroDBFlores, confirmed — the v2 configDefault has the exact same pattern: it returns ConfigDefault (with a nil Enforcer) immediately when no args are provided, so your v3 implementation is consistent with it.

That said, the nil-enforcer risk on the no-arg path is technically shared by both v2 and v3. If you'd ever like to address it, fixing both together in a follow-up would be the natural place to do so. Happy to open a tracking issue for that if you'd like. Otherwise, I'll defer to your judgment here.

🐇

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
v3/casbin/config_v3.go (1)

49-51: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

configDefaultV3 returns nil Enforcer when called with no arguments.

When no config is provided, the function returns ConfigV3Default as-is, bypassing enforcer creation at lines 56-71. Since ConfigV3Default.Enforcer is uninitialized (nil), downstream middleware will panic on any enforcer method call.

The fix is to merge both paths so the Enforcer-nil check always runs, creating a default enforcer from ModelFilePath and PolicyAdapter.

🔧 Proposed fix
 func configDefaultV3(config ...ConfigV3) (ConfigV3, error) {
-	// Return default config if nothing provided
-	if len(config) < 1 {
-		return ConfigV3Default, nil
-	}
-
-	// Override default config
-	cfg := config[0]
+	cfg := ConfigV3Default
+	if len(config) > 0 {
+		cfg = config[0]
+	}
 
 	if cfg.Enforcer == nil {
 		if cfg.ModelFilePath == "" {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/casbin/config_v3.go` around lines 49 - 51, The function configDefaultV3
returns ConfigV3Default directly when called with no args, leaving
ConfigV3Default.Enforcer nil; modify configDefaultV3 so both the empty-config
and non-empty-config paths converge before the Enforcer nil check: always check
cfg.Enforcer and if it's nil create and assign a new Enforcer using
cfg.ModelFilePath and cfg.PolicyAdapter (the same logic currently in the
Enforcer-nil branch around the create-enforcer code), ensuring ConfigV3Default
is not returned with a nil Enforcer.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@v3/casbin/config_v3.go`:
- Around line 49-51: The function configDefaultV3 returns ConfigV3Default
directly when called with no args, leaving ConfigV3Default.Enforcer nil; modify
configDefaultV3 so both the empty-config and non-empty-config paths converge
before the Enforcer nil check: always check cfg.Enforcer and if it's nil create
and assign a new Enforcer using cfg.ModelFilePath and cfg.PolicyAdapter (the
same logic currently in the Enforcer-nil branch around the create-enforcer
code), ensuring ConfigV3Default is not returned with a nil Enforcer.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ff6fc50c-e8b9-46d0-a2f0-cbcc62249188

📥 Commits

Reviewing files that changed from the base of the PR and between 8ee8380 and ca7b8bf.

⛔ Files ignored due to path filters (1)
  • v3/casbin/go.mod is excluded by !**/*.mod
📒 Files selected for processing (1)
  • v3/casbin/config_v3.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants