Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 45 additions & 5 deletions effector/default_effector.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
}

// MergeEffects merges all matching results collected by the enforcer into a single decision.
func (e *DefaultEffector) MergeEffects(expr string, effects []Effect, matches []float64, policyIndex int, policyLength int) (Effect, int, error) {

Check failure on line 34 in effector/default_effector.go

View workflow job for this annotation

GitHub Actions / golangci

Function 'MergeEffects' has too many statements (67 > 50) (funlen)
result := Indeterminate
explainIndex := -1

Expand All @@ -40,22 +40,54 @@
if matches[policyIndex] == 0 {
break
}
// only check the current policyIndex
// only check the current policyIndex (priority order: Allow > RateLimit)
if effects[policyIndex] == Allow {
result = Allow
explainIndex = policyIndex
break
}
if effects[policyIndex] == RateLimit {
result = RateLimit
explainIndex = policyIndex
break
}
case constant.DenyOverrideEffect:
// only check the current policyIndex
if matches[policyIndex] != 0 && effects[policyIndex] == Deny {
result = Deny
explainIndex = policyIndex
break
}
// if no deny rules are matched at last, then allow
// if no deny rules are matched at last, check for allow or rate_limit
if policyIndex == policyLength-1 {

Check failure on line 62 in effector/default_effector.go

View workflow job for this annotation

GitHub Actions / golangci

`if policyIndex == policyLength-1` has complex nested blocks (complexity: 8) (nestif)
result = Allow
// Check all matched policies for allow first, then rate_limit (priority order: Allow > RateLimit)
for i := range effects {
if matches[i] == 0 {
continue
}
if effects[i] == Allow {
result = Allow
explainIndex = i
break
}
}
// If no allow found, check for rate_limit
if result == Indeterminate {
for i := range effects {
if matches[i] == 0 {
continue
}
if effects[i] == RateLimit {
result = RateLimit
explainIndex = i
break
}
}
}
// DenyOverride defaults to Allow if no deny rules matched (matches original behavior)
if result == Indeterminate {
result = Allow
}
}
case constant.AllowAndDenyEffect:
// short-circuit if matched deny rule
Expand All @@ -71,7 +103,7 @@
// choose not to short-circuit
return result, explainIndex, nil
}
// merge all effects at last
// merge all effects at last (priority order: Allow > RateLimit)
for i, eft := range effects {
if matches[i] == 0 {
continue
Expand All @@ -83,9 +115,15 @@
explainIndex = i
break
}
if eft == RateLimit {
result = RateLimit
// set hit rule to first matched rate_limit rule
explainIndex = i
break
}
}
case constant.PriorityEffect, constant.SubjectPriorityEffect:
// reverse merge, short-circuit may be earlier
// reverse merge, short-circuit may be earlier (priority order: Allow > RateLimit > Deny)
for i := len(effects) - 1; i >= 0; i-- {
if matches[i] == 0 {
continue
Expand All @@ -94,6 +132,8 @@
if effects[i] != Indeterminate {
if effects[i] == Allow {
result = Allow
} else if effects[i] == RateLimit {
result = RateLimit
} else {
result = Deny
}
Expand Down
1 change: 1 addition & 0 deletions effector/effector.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const (
Allow Effect = iota
Indeterminate
Deny
RateLimit
)

// Effector is the interface for Casbin effectors.
Expand Down
4 changes: 3 additions & 1 deletion enforcer.go
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,8 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac
policyEffects[policyIndex] = effector.Allow
} else if eft == "deny" {
policyEffects[policyIndex] = effector.Deny
} else if eft == "rate_limit" {
policyEffects[policyIndex] = effector.RateLimit
} else {
policyEffects[policyIndex] = effector.Indeterminate
}
Expand Down Expand Up @@ -855,7 +857,7 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac

// effect -> result
result := false
if effect == effector.Allow {
if effect == effector.Allow || effect == effector.RateLimit {
result = true
}
e.logger.LogEnforce(expString, rvals, result, logExplains)
Expand Down
11 changes: 11 additions & 0 deletions examples/rate_limit_deny_override_model.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[request_definition]
r = sub, obj, act

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

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

[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
4 changes: 4 additions & 0 deletions examples/rate_limit_deny_override_policy.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
p, alice, data1, read, allow
p, bob, data2, write, rate_limit
p, charlie, data3, read, deny
p, david, data4, write, rate_limit
14 changes: 14 additions & 0 deletions examples/rate_limit_model.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[request_definition]
r = sub, obj, act

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

[role_definition]
g = _, _

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

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
7 changes: 7 additions & 0 deletions examples/rate_limit_policy.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
p, alice, data1, read, allow
p, bob, data2, write, rate_limit
p, data2_admin, data2, read, allow
p, data2_admin, data2, write, allow
p, charlie, data3, read, rate_limit

g, alice, data2_admin
19 changes: 19 additions & 0 deletions model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,25 @@ func TestRBACModelWithOnlyDeny(t *testing.T) {
testEnforce(t, e, "alice", "data2", "write", false)
}

func TestRBACModelWithRateLimit(t *testing.T) {
e, _ := NewEnforcer("examples/rate_limit_model.conf", "examples/rate_limit_policy.csv")

testEnforce(t, e, "alice", "data1", "read", true)
testEnforce(t, e, "bob", "data2", "write", true) // rate_limit effect should return true
testEnforce(t, e, "charlie", "data3", "read", true) // rate_limit effect should return true
testEnforce(t, e, "alice", "data2", "read", true)
testEnforce(t, e, "alice", "data2", "write", true)
}

func TestRateLimitWithDenyOverride(t *testing.T) {
e, _ := NewEnforcer("examples/rate_limit_deny_override_model.conf", "examples/rate_limit_deny_override_policy.csv")

testEnforce(t, e, "alice", "data1", "read", true) // allow effect
testEnforce(t, e, "bob", "data2", "write", true) // rate_limit effect should return true
testEnforce(t, e, "charlie", "data3", "read", false) // deny effect should return false
testEnforce(t, e, "david", "data4", "write", true) // rate_limit effect should return true
}

func TestRBACModelWithCustomData(t *testing.T) {
e, _ := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv")

Expand Down
Loading