Skip to content

Commit

Permalink
feat: support all types for JSON request in ABAC (#1343)
Browse files Browse the repository at this point in the history
  • Loading branch information
abichinger committed Nov 24, 2023
1 parent 4d1349a commit 6703d2f
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 43 deletions.
50 changes: 12 additions & 38 deletions enforcer.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package casbin
import (
"errors"
"fmt"
"regexp"
"runtime/debug"
"strings"
"sync"
Expand All @@ -32,7 +31,6 @@ import (
"github.com/casbin/casbin/v2/util"

"github.com/casbin/govaluate"
"github.com/tidwall/gjson"
)

// Enforcer is the main interface for authorization enforcement and policy management.
Expand Down Expand Up @@ -637,7 +635,17 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac
}

if e.acceptJsonRequest {
expString = requestJsonReplace(expString, rTokens, rvals)
// try to parse all request values from json to map[string]interface{}
// skip if there is an error
for i, rval := range rvals {
switch rval := rval.(type) {
case string:
mapValue, err := util.JsonToMap(rval)
if err == nil {
rvals[i] = mapValue
}
}
}
}

parameters := enforceParameters{
Expand Down Expand Up @@ -685,16 +693,7 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac
pvals)
}

if e.acceptJsonRequest {
pvalsCopy := make([]string, len(pvals))
copy(pvalsCopy, pvals)
for i, pStr := range pvalsCopy {
pvalsCopy[i] = requestJsonReplace(util.EscapeAssertion(pStr), rTokens, rvals)
}
parameters.pVals = pvalsCopy
} else {
parameters.pVals = pvals
}
parameters.pVals = pvals

result, err := expression.Eval(parameters)
// log.LogPrint("Result: ", result)
Expand Down Expand Up @@ -796,31 +795,6 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac
return result, nil
}

var requestObjectRegex = regexp.MustCompile(`r[_.][A-Za-z_0-9]+\.[A-Za-z_0-9.]+[A-Za-z_0-9]`)
var requestObjectRegexPrefix = regexp.MustCompile(`r[_.][A-Za-z_0-9]+\.`)

// requestJsonReplace used to support request parameters of type json
// It will replace the access of the request object in matchers or policy with the actual value in the request json parameter
// For example: request sub = `{"Owner": "alice", "Age": 30}`
// policy: p, r.sub.Age > 18, /data1, read ==> p, 30 > 18, /data1, read
// matchers: m = r.sub == r.obj.Owner ==> m = r.sub == "alice"
func requestJsonReplace(str string, rTokens map[string]int, rvals []interface{}) string {
matches := requestObjectRegex.FindAllString(str, -1)
for _, matchesStr := range matches {
prefix := requestObjectRegexPrefix.FindString(matchesStr)
jsonPath := strings.TrimPrefix(matchesStr, prefix)
tokenIndex := rTokens[prefix[:len(prefix)-1]]
if jsonStr, ok := rvals[tokenIndex].(string); ok {
newStr := gjson.Get(jsonStr, jsonPath).String()
if !util.IsNumeric(newStr) {
newStr = `"` + newStr + `"`
}
str = strings.Replace(str, matchesStr, newStr, -1)
}
}
return str
}

func (e *Enforcer) getAndStoreMatcherExpression(hasEval bool, expString string, functions map[string]govaluate.ExpressionFunction) (*govaluate.EvaluableExpression, error) {
var expression *govaluate.EvaluableExpression
var err error
Expand Down
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ module github.com/casbin/casbin/v2
require (
github.com/casbin/govaluate v1.1.0
github.com/golang/mock v1.4.4
github.com/tidwall/gjson v1.14.4
)

go 1.13
32 changes: 32 additions & 0 deletions model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package casbin

import (
"encoding/json"
"fmt"
"testing"

Expand Down Expand Up @@ -474,6 +475,37 @@ func TestABACMapRequest(t *testing.T) {
testEnforce(t, e, "bob", data2, "write", true)
}

func TestABACTypes(t *testing.T) {
e, _ := NewEnforcer("examples/abac_model.conf")
matcher := `"moderator" IN r.sub.Roles && r.sub.Enabled == true && r.sub.Age >= 21 && r.sub.Name != "foo"`
e.GetModel()["m"]["m"].Value = util.RemoveComments(util.EscapeAssertion(matcher))

structRequest := struct {
Roles []interface{}
Enabled bool
Age int
Name string
}{
Roles: []interface{}{"user", "moderator"},
Enabled: true,
Age: 30,
Name: "alice",
}
testEnforce(t, e, structRequest, "", "", true)

mapRequest := map[string]interface{}{
"Roles": []interface{}{"user", "moderator"},
"Enabled": true,
"Age": 30,
"Name": "alice",
}
testEnforce(t, e, mapRequest, nil, "", true)

e.EnableAcceptJsonRequest(true)
jsonRequest, _ := json.Marshal(mapRequest)
testEnforce(t, e, string(jsonRequest), "", "", true)
}

func TestABACJsonRequest(t *testing.T) {
e, _ := NewEnforcer("examples/abac_model.conf")
e.EnableAcceptJsonRequest(true)
Expand Down
12 changes: 8 additions & 4 deletions util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package util

import (
"encoding/json"
"regexp"
"sort"
"strings"
Expand All @@ -25,10 +26,13 @@ var evalReg = regexp.MustCompile(`\beval\((?P<rule>[^)]*)\)`)

var escapeAssertionRegex = regexp.MustCompile(`\b((r|p)[0-9]*)\.`)

var numericRegex = regexp.MustCompile(`^-?\d+(?:\.\d+)?$`)

func IsNumeric(s string) bool {
return numericRegex.MatchString(s)
func JsonToMap(jsonStr string) (map[string]interface{}, error) {
result := make(map[string]interface{})
err := json.Unmarshal([]byte(jsonStr), &result)
if err != nil {
return result, err
}
return result, nil
}

// EscapeAssertion escapes the dots in the assertion, because the expression evaluation doesn't support such variable names.
Expand Down

1 comment on commit 6703d2f

@github-actions
Copy link

Choose a reason for hiding this comment

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

Benchmark

Benchmark suite Current: 6703d2f Previous: 4d1349a Ratio
BenchmarkCachedRaw - ns/op 17.65 ns/op 17.55 ns/op 1.01
BenchmarkCachedRaw - B/op 0 B/op 0 B/op NaN
BenchmarkCachedRaw - allocs/op 0 allocs/op 0 allocs/op NaN
BenchmarkCachedBasicModel - ns/op 165.6 ns/op 165.3 ns/op 1.00
BenchmarkCachedBasicModel - B/op 104 B/op 104 B/op 1
BenchmarkCachedBasicModel - allocs/op 4 allocs/op 4 allocs/op 1
BenchmarkCachedRBACModel - ns/op 161.6 ns/op 168.9 ns/op 0.96
BenchmarkCachedRBACModel - B/op 104 B/op 104 B/op 1
BenchmarkCachedRBACModel - allocs/op 4 allocs/op 4 allocs/op 1
BenchmarkCachedRBACModelSmall - ns/op 176.6 ns/op 177.5 ns/op 0.99
BenchmarkCachedRBACModelSmall - B/op 104 B/op 104 B/op 1
BenchmarkCachedRBACModelSmall - allocs/op 4 allocs/op 4 allocs/op 1
BenchmarkCachedRBACModelMedium - ns/op 188.1 ns/op 179.7 ns/op 1.05
BenchmarkCachedRBACModelMedium - B/op 104 B/op 104 B/op 1
BenchmarkCachedRBACModelMedium - allocs/op 4 allocs/op 4 allocs/op 1
BenchmarkCachedRBACModelLarge - ns/op 156.4 ns/op 156.6 ns/op 1.00
BenchmarkCachedRBACModelLarge - B/op 96 B/op 96 B/op 1
BenchmarkCachedRBACModelLarge - allocs/op 3 allocs/op 3 allocs/op 1
BenchmarkCachedRBACModelWithResourceRoles - ns/op 163.9 ns/op 167.3 ns/op 0.98
BenchmarkCachedRBACModelWithResourceRoles - B/op 104 B/op 104 B/op 1
BenchmarkCachedRBACModelWithResourceRoles - allocs/op 4 allocs/op 4 allocs/op 1
BenchmarkCachedRBACModelWithDomains - ns/op 178.4 ns/op 179.6 ns/op 0.99
BenchmarkCachedRBACModelWithDomains - B/op 120 B/op 120 B/op 1
BenchmarkCachedRBACModelWithDomains - allocs/op 4 allocs/op 4 allocs/op 1
BenchmarkCachedABACModel - ns/op 2979 ns/op 3048 ns/op 0.98
BenchmarkCachedABACModel - B/op 1538 B/op 1544 B/op 1.00
BenchmarkCachedABACModel - allocs/op 18 allocs/op 18 allocs/op 1
BenchmarkCachedKeyMatchModel - ns/op 176.2 ns/op 183.9 ns/op 0.96
BenchmarkCachedKeyMatchModel - B/op 152 B/op 152 B/op 1
BenchmarkCachedKeyMatchModel - allocs/op 4 allocs/op 4 allocs/op 1
BenchmarkCachedRBACModelWithDeny - ns/op 168.8 ns/op 168.2 ns/op 1.00
BenchmarkCachedRBACModelWithDeny - B/op 104 B/op 104 B/op 1
BenchmarkCachedRBACModelWithDeny - allocs/op 4 allocs/op 4 allocs/op 1
BenchmarkCachedPriorityModel - ns/op 162.5 ns/op 170.7 ns/op 0.95
BenchmarkCachedPriorityModel - B/op 104 B/op 104 B/op 1
BenchmarkCachedPriorityModel - allocs/op 4 allocs/op 4 allocs/op 1
BenchmarkCachedWithEnforceContext - ns/op 295.5 ns/op 312.8 ns/op 0.94
BenchmarkCachedWithEnforceContext - B/op 240 B/op 240 B/op 1
BenchmarkCachedWithEnforceContext - allocs/op 5 allocs/op 5 allocs/op 1
BenchmarkCachedRBACModelMediumParallel - ns/op 121.7 ns/op 123.9 ns/op 0.98
BenchmarkCachedRBACModelMediumParallel - B/op 105 B/op 105 B/op 1
BenchmarkCachedRBACModelMediumParallel - allocs/op 4 allocs/op 4 allocs/op 1
BenchmarkHasPolicySmall - ns/op 444.1 ns/op 460.3 ns/op 0.96
BenchmarkHasPolicySmall - B/op 150 B/op 150 B/op 1
BenchmarkHasPolicySmall - allocs/op 6 allocs/op 6 allocs/op 1
BenchmarkHasPolicyMedium - ns/op 483.9 ns/op 499.7 ns/op 0.97
BenchmarkHasPolicyMedium - B/op 157 B/op 157 B/op 1
BenchmarkHasPolicyMedium - allocs/op 6 allocs/op 6 allocs/op 1
BenchmarkHasPolicyLarge - ns/op 503.8 ns/op 509.3 ns/op 0.99
BenchmarkHasPolicyLarge - B/op 165 B/op 165 B/op 1
BenchmarkHasPolicyLarge - allocs/op 7 allocs/op 7 allocs/op 1
BenchmarkAddPolicySmall - ns/op 488.2 ns/op 467.3 ns/op 1.04
BenchmarkAddPolicySmall - B/op 152 B/op 152 B/op 1
BenchmarkAddPolicySmall - allocs/op 6 allocs/op 6 allocs/op 1
BenchmarkAddPolicyMedium - ns/op 563.3 ns/op 627.4 ns/op 0.90
BenchmarkAddPolicyMedium - B/op 173 B/op 173 B/op 1
BenchmarkAddPolicyMedium - allocs/op 7 allocs/op 7 allocs/op 1
BenchmarkAddPolicyLarge - ns/op 1218 ns/op 1278 ns/op 0.95
BenchmarkAddPolicyLarge - B/op 473 B/op 473 B/op 1
BenchmarkAddPolicyLarge - allocs/op 9 allocs/op 9 allocs/op 1
BenchmarkRemovePolicySmall - ns/op 488.5 ns/op 481.4 ns/op 1.01
BenchmarkRemovePolicySmall - B/op 166 B/op 166 B/op 1
BenchmarkRemovePolicySmall - allocs/op 7 allocs/op 7 allocs/op 1
BenchmarkRemovePolicyMedium - ns/op 554.2 ns/op 537.3 ns/op 1.03
BenchmarkRemovePolicyMedium - B/op 176 B/op 176 B/op 1
BenchmarkRemovePolicyMedium - allocs/op 7 allocs/op 7 allocs/op 1
BenchmarkRemovePolicyLarge - ns/op 1212 ns/op 1243 ns/op 0.98
BenchmarkRemovePolicyLarge - B/op 291 B/op 289 B/op 1.01
BenchmarkRemovePolicyLarge - allocs/op 13 allocs/op 13 allocs/op 1
BenchmarkRaw - ns/op 17.68 ns/op 17.53 ns/op 1.01
BenchmarkRaw - B/op 0 B/op 0 B/op NaN
BenchmarkRaw - allocs/op 0 allocs/op 0 allocs/op NaN
BenchmarkBasicModel - ns/op 3719 ns/op 3706 ns/op 1.00
BenchmarkBasicModel - B/op 1508 B/op 1510 B/op 1.00
BenchmarkBasicModel - allocs/op 17 allocs/op 17 allocs/op 1
BenchmarkRBACModel - ns/op 5489 ns/op 5613 ns/op 0.98
BenchmarkRBACModel - B/op 2065 B/op 2070 B/op 1.00
BenchmarkRBACModel - allocs/op 35 allocs/op 35 allocs/op 1
BenchmarkRBACModelSizes/small - ns/op 49969 ns/op 49082 ns/op 1.02
BenchmarkRBACModelSizes/small - B/op 20271 B/op 20214 B/op 1.00
BenchmarkRBACModelSizes/small - allocs/op 480 allocs/op 480 allocs/op 1
BenchmarkRBACModelSizes/medium - ns/op 511984 ns/op 516834 ns/op 0.99
BenchmarkRBACModelSizes/medium - B/op 191257 B/op 191758 B/op 1.00
BenchmarkRBACModelSizes/medium - allocs/op 4829 allocs/op 4830 allocs/op 1.00
BenchmarkRBACModelSizes/large - ns/op 5195604 ns/op 5709390 ns/op 0.91
BenchmarkRBACModelSizes/large - B/op 1906391 B/op 1899742 B/op 1.00
BenchmarkRBACModelSizes/large - allocs/op 48353 allocs/op 48170 allocs/op 1.00
BenchmarkRBACModelSmall - ns/op 59230 ns/op 61393 ns/op 0.96
BenchmarkRBACModelSmall - B/op 20352 B/op 20339 B/op 1.00
BenchmarkRBACModelSmall - allocs/op 615 allocs/op 615 allocs/op 1
BenchmarkRBACModelMedium - ns/op 576178 ns/op 579667 ns/op 0.99
BenchmarkRBACModelMedium - B/op 194638 B/op 194721 B/op 1.00
BenchmarkRBACModelMedium - allocs/op 6020 allocs/op 6021 allocs/op 1.00
BenchmarkRBACModelLarge - ns/op 6073531 ns/op 6169723 ns/op 0.98
BenchmarkRBACModelLarge - B/op 1940496 B/op 1941098 B/op 1.00
BenchmarkRBACModelLarge - allocs/op 60599 allocs/op 60621 allocs/op 1.00
BenchmarkRBACModelWithResourceRoles - ns/op 4605 ns/op 4716 ns/op 0.98
BenchmarkRBACModelWithResourceRoles - B/op 1846 B/op 1847 B/op 1.00
BenchmarkRBACModelWithResourceRoles - allocs/op 27 allocs/op 27 allocs/op 1
BenchmarkRBACModelWithDomains - ns/op 5123 ns/op 5296 ns/op 0.97
BenchmarkRBACModelWithDomains - B/op 1824 B/op 1828 B/op 1.00
BenchmarkRBACModelWithDomains - allocs/op 25 allocs/op 25 allocs/op 1
BenchmarkABACModel - ns/op 2858 ns/op 2876 ns/op 0.99
BenchmarkABACModel - B/op 1536 B/op 1536 B/op 1
BenchmarkABACModel - allocs/op 17 allocs/op 17 allocs/op 1
BenchmarkABACRuleModel - ns/op 3982954 ns/op 3977634 ns/op 1.00
BenchmarkABACRuleModel - B/op 1330745 B/op 1327819 B/op 1.00
BenchmarkABACRuleModel - allocs/op 40092 allocs/op 40092 allocs/op 1
BenchmarkKeyMatchModel - ns/op 6212 ns/op 6287 ns/op 0.99
BenchmarkKeyMatchModel - B/op 3064 B/op 3072 B/op 1.00
BenchmarkKeyMatchModel - allocs/op 37 allocs/op 37 allocs/op 1
BenchmarkRBACModelWithDeny - ns/op 6982 ns/op 7103 ns/op 0.98
BenchmarkRBACModelWithDeny - B/op 2483 B/op 2484 B/op 1.00
BenchmarkRBACModelWithDeny - allocs/op 49 allocs/op 49 allocs/op 1
BenchmarkPriorityModel - ns/op 4241 ns/op 4310 ns/op 0.98
BenchmarkPriorityModel - B/op 1759 B/op 1765 B/op 1.00
BenchmarkPriorityModel - allocs/op 22 allocs/op 22 allocs/op 1
BenchmarkRBACModelWithDomainPatternLarge - ns/op 23818 ns/op 24622 ns/op 0.97
BenchmarkRBACModelWithDomainPatternLarge - B/op 16762 B/op 16756 B/op 1.00
BenchmarkRBACModelWithDomainPatternLarge - allocs/op 164 allocs/op 164 allocs/op 1
BenchmarkRoleManagerSmall - ns/op 69717 ns/op 70429 ns/op 0.99
BenchmarkRoleManagerSmall - B/op 11955 B/op 11955 B/op 1
BenchmarkRoleManagerSmall - allocs/op 797 allocs/op 797 allocs/op 1
BenchmarkRoleManagerMedium - ns/op 741601 ns/op 755138 ns/op 0.98
BenchmarkRoleManagerMedium - B/op 125914 B/op 125915 B/op 1.00
BenchmarkRoleManagerMedium - allocs/op 8741 allocs/op 8741 allocs/op 1
BenchmarkRoleManagerLarge - ns/op 8214236 ns/op 9329911 ns/op 0.88
BenchmarkRoleManagerLarge - B/op 1349923 B/op 1349925 B/op 1.00
BenchmarkRoleManagerLarge - allocs/op 89741 allocs/op 89741 allocs/op 1
BenchmarkBuildRoleLinksWithPatternLarge - ns/op 6040649458 ns/op 6212283114 ns/op 0.97
BenchmarkBuildRoleLinksWithPatternLarge - B/op 5350679168 B/op 5348304072 B/op 1.00
BenchmarkBuildRoleLinksWithPatternLarge - allocs/op 60950665 allocs/op 60949962 allocs/op 1.00
BenchmarkBuildRoleLinksWithDomainPatternLarge - ns/op 163583356 ns/op 169952935 ns/op 0.96
BenchmarkBuildRoleLinksWithDomainPatternLarge - B/op 141030638 B/op 141455876 B/op 1.00
BenchmarkBuildRoleLinksWithDomainPatternLarge - allocs/op 1676469 allocs/op 1676533 allocs/op 1.00
BenchmarkBuildRoleLinksWithPatternAndDomainPatternLarge - ns/op 6200676971 ns/op 6435645526 ns/op 0.96
BenchmarkBuildRoleLinksWithPatternAndDomainPatternLarge - B/op 5472251672 B/op 5483027224 B/op 1.00
BenchmarkBuildRoleLinksWithPatternAndDomainPatternLarge - allocs/op 62557899 allocs/op 62559972 allocs/op 1.00
BenchmarkHasLinkWithPatternLarge - ns/op 11049 ns/op 10668 ns/op 1.04
BenchmarkHasLinkWithPatternLarge - B/op 7599 B/op 7603 B/op 1.00
BenchmarkHasLinkWithPatternLarge - allocs/op 111 allocs/op 111 allocs/op 1
BenchmarkHasLinkWithDomainPatternLarge - ns/op 498.3 ns/op 497.3 ns/op 1.00
BenchmarkHasLinkWithDomainPatternLarge - B/op 80 B/op 80 B/op 1
BenchmarkHasLinkWithDomainPatternLarge - allocs/op 5 allocs/op 5 allocs/op 1
BenchmarkHasLinkWithPatternAndDomainPatternLarge - ns/op 10253 ns/op 10322 ns/op 0.99
BenchmarkHasLinkWithPatternAndDomainPatternLarge - B/op 7602 B/op 7612 B/op 1.00
BenchmarkHasLinkWithPatternAndDomainPatternLarge - allocs/op 111 allocs/op 111 allocs/op 1

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.