From 96d49cfdccdbf0955ac31ed9c8360f3ebedcbf9c Mon Sep 17 00:00:00 2001 From: Andreas Bichinger Date: Thu, 5 May 2022 14:31:33 +0200 Subject: [PATCH] perf: add eval to function map Signed-off-by: Andreas Bichinger --- enforcer.go | 60 +++++++++++++++++++++++++------------------------ model_b_test.go | 14 ++++++++++++ 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/enforcer.go b/enforcer.go index f5a0a8a0..8e7f7446 100644 --- a/enforcer.go +++ b/enforcer.go @@ -517,16 +517,6 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac expString = util.RemoveComments(util.EscapeAssertion(matcher)) } - var expression *govaluate.EvaluableExpression - hasEval := util.HasEval(expString) - - if !hasEval { - expression, err = govaluate.NewEvaluableExpressionWithFunctions(expString, functions) - if err != nil { - return false, err - } - } - rTokens := make(map[string]int, len(e.model["r"][rType].Tokens)) for i, token := range e.model["r"][rType].Tokens { rTokens[token] = i @@ -543,6 +533,18 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac pTokens: pTokens, } + var expression *govaluate.EvaluableExpression + hasEval := util.HasEval(expString) + + if hasEval { + functions["eval"] = generateEvalFunction(functions, ¶meters) + } + + expression, err = govaluate.NewEvaluableExpressionWithFunctions(expString, functions) + if err != nil { + return false, err + } + if len(e.model["r"][rType].Tokens) != len(rvals) { return false, fmt.Errorf( "invalid request size: expected %d, got %d, rvals: %v", @@ -573,25 +575,6 @@ func (e *Enforcer) enforce(matcher string, explains *[]string, rvals ...interfac parameters.pVals = pvals - if hasEval { - ruleNames := util.GetEvalValue(expString) - replacements := make(map[string]string) - for _, ruleName := range ruleNames { - if j, ok := parameters.pTokens[ruleName]; ok { - rule := util.EscapeAssertion(pvals[j]) - // Increase the evaluate priority of the rule - replacements[ruleName] = "(" + rule + ")" - } else { - return false, errors.New("please make sure rule exists in policy when using eval() in matcher") - } - } - expWithRule := util.ReplaceEvalWithMap(expString, replacements) - expression, err = govaluate.NewEvaluableExpressionWithFunctions(expWithRule, functions) - if err != nil { - return false, fmt.Errorf("p.sub_rule should satisfy the syntax of matcher: %s", err) - } - } - result, err := expression.Eval(parameters) // log.LogPrint("Result: ", result) @@ -792,3 +775,22 @@ func (p enforceParameters) Get(name string) (interface{}, error) { return nil, errors.New("No parameter '" + name + "' found.") } } + +func generateEvalFunction(functions map[string]govaluate.ExpressionFunction, parameters *enforceParameters) govaluate.ExpressionFunction { + return func(args ...interface{}) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf("Function eval(subrule string) expected %d arguments, but got %d", 1, len(args)) + } + + expression, ok := args[0].(string) + if !ok { + return nil, errors.New("Argument of eval(subrule string) must be a string") + } + expression = util.EscapeAssertion(expression) + expr, err := govaluate.NewEvaluableExpressionWithFunctions(expression, functions) + if err != nil { + return nil, fmt.Errorf("Error while parsing eval parameter: %s, %s", expression, err.Error()) + } + return expr.Eval(parameters) + } +} diff --git a/model_b_test.go b/model_b_test.go index 745565a2..ef959b1f 100644 --- a/model_b_test.go +++ b/model_b_test.go @@ -239,6 +239,20 @@ func BenchmarkABACModel(b *testing.B) { } } +func BenchmarkABACRuleModel(b *testing.B) { + e, _ := NewEnforcer("examples/abac_rule_model.conf", false) + sub := newTestSubject("alice", 18) + + for i := 0; i < 1000; i++ { + _, _ = e.AddPolicy("r.sub.Age > 20", fmt.Sprintf("data%d", i), "read") + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _ = e.Enforce(sub, "data100", "read") + } +} + func BenchmarkKeyMatchModel(b *testing.B) { e, _ := NewEnforcer("examples/keymatch_model.conf", "examples/keymatch_policy.csv", false)