This is an automated email from the ASF dual-hosted git repository.
hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin-lua-casbin.git
The following commit(s) were added to refs/heads/master by this push:
new 711cb48 fix: keyMatch4 error on patterns without {token}
711cb48 is described below
commit 711cb48cdaeb2730304fad7cc5b41d06869111d0
Author: Yang Luo <[email protected]>
AuthorDate: Thu Aug 6 21:21:32 2026 +0800
fix: keyMatch4 error on patterns without {token}
---
examples/rbac_with_keymatch4_model.conf | 14 ++++++++++++++
src/main/CoreEnforcer.lua | 22 ++++++++++++++++++++--
src/model/FunctionMap.lua | 2 +-
src/util/BuiltInFunctions.lua | 12 ++++++++++--
tests/main/enforcer_spec.lua | 12 ++++++++++++
tests/util/built_in_functions_spec.lua | 11 +++++++++++
6 files changed, 68 insertions(+), 5 deletions(-)
diff --git a/examples/rbac_with_keymatch4_model.conf
b/examples/rbac_with_keymatch4_model.conf
new file mode 100644
index 0000000..da39667
--- /dev/null
+++ b/examples/rbac_with_keymatch4_model.conf
@@ -0,0 +1,14 @@
+[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) && keyMatch4(r.obj, p.obj) && regexMatch(r.act, p.act)
diff --git a/src/main/CoreEnforcer.lua b/src/main/CoreEnforcer.lua
index 659a9b9..2cb464f 100644
--- a/src/main/CoreEnforcer.lua
+++ b/src/main/CoreEnforcer.lua
@@ -25,6 +25,24 @@ local Util = require("src.util.Util")
local luaxp = require("modules.luaxp")
+--[[
+ * describeEvalError turns a luaxp error into a readable string.
+ * luaxp reports its own compile/evaluation errors as a table with a
+ * "message" field, but an error raised inside a matcher function (a
+ * built-in like keyMatch4, or a user-registered one) is passed through
+ * as a plain string. Reading .message unconditionally hid the real
+ * cause behind "attempt to concatenate field 'message' (a nil value)".
+ *
+ * @param err the second value returned by luaxp.run/luaxp.evaluate.
+ * @return a string describing the error.
+]]
+local function describeEvalError(err)
+ if type(err) == "table" and err.message then
+ return tostring(err.message)
+ end
+ return tostring(err)
+end
+
--[[
* checkAdapterResult raises a Lua error when an adapter reports a failure
* through the "return nil/false, err" convention instead of raising it.
@@ -463,7 +481,7 @@ function CoreEnforcer:enforceEx(...)
res, err = luaxp.evaluate(tExpString, context)
end
if err then
- error("evaluation error: " .. err.message)
+ error("evaluation error: " .. describeEvalError(err))
end
local c = true
@@ -508,7 +526,7 @@ function CoreEnforcer:enforceEx(...)
local res, err = luaxp.run(compiledExpression, context)
if err then
- error("evaluation error: " .. err.message)
+ error("evaluation error: " .. describeEvalError(err))
end
if res then
diff --git a/src/model/FunctionMap.lua b/src/model/FunctionMap.lua
index 5939124..9984a41 100644
--- a/src/model/FunctionMap.lua
+++ b/src/model/FunctionMap.lua
@@ -21,7 +21,7 @@ local FunctionMap = {
["keyGet2"] = BuiltInFunctions.keyGet2Func,
["keyMatch3"] = BuiltInFunctions.keyMatch3Func,
["keyMatch4"] = BuiltInFunctions.keyMatch4Func,
- ["keyMatch5"] = BuiltInFunctions.keyMatch4Func,
+ ["keyMatch5"] = BuiltInFunctions.keyMatch5Func,
["regexMatch"] = BuiltInFunctions.regexMatchFunc,
["IPMatch"] = BuiltInFunctions.IPMatchFunc,
["globMatch"] = BuiltInFunctions.globMatchFunc,
diff --git a/src/util/BuiltInFunctions.lua b/src/util/BuiltInFunctions.lua
index 16b0f4a..c39d4d0 100644
--- a/src/util/BuiltInFunctions.lua
+++ b/src/util/BuiltInFunctions.lua
@@ -144,10 +144,18 @@ function BuiltInFunctions.keyMatch4(key1, key2)
return "([^/]+)"
end
key2=string.gsub(key2,"{([^/]+)}",repl)
- if string.match(key1, key2)==nil then
- return false
+ key2="^" .. key2 .. "$"
+
+ -- Without any {token}, the pattern has no captures, so string.match would
+ -- return the whole match instead of a (possibly empty) list of values.
+ if #tokens==0 then
+ return string.match(key1, key2)~=nil
end
+
local matches={string.match(key1, key2)}
+ if matches[1]==nil then
+ return false
+ end
if #tokens~= #matches then
error("KeyMatch4: number of tokens is not equal to number of values")
end
diff --git a/tests/main/enforcer_spec.lua b/tests/main/enforcer_spec.lua
index 3337ab3..54977a9 100644
--- a/tests/main/enforcer_spec.lua
+++ b/tests/main/enforcer_spec.lua
@@ -94,6 +94,18 @@ describe("Enforcer tests", function ()
assert.is.True(e:enforce("alice", "/alice_data2/123/using/456", "GET"))
end)
+ it("rbac with keyMatch4 test", function ()
+ local model = path .. "/examples/rbac_with_keymatch4_model.conf"
+ local policy = path .. "/examples/rbac_policy.csv"
+
+ local e = Enforcer:new(model, policy)
+ assert.is.True(e:enforce("alice", "data1", "read"))
+ assert.is.False(e:enforce("alice", "data1", "write"))
+ assert.is.True(e:enforce("alice", "data2", "write"))
+ assert.is.False(e:enforce("bob", "data1", "read"))
+ assert.is.True(e:enforce("bob", "data2", "write"))
+ end)
+
it("priority test", function ()
local model = path .. "/examples/priority_model.conf"
local policy = path .. "/examples/priority_policy.csv"
diff --git a/tests/util/built_in_functions_spec.lua
b/tests/util/built_in_functions_spec.lua
index f7ef8aa..44f2e30 100644
--- a/tests/util/built_in_functions_spec.lua
+++ b/tests/util/built_in_functions_spec.lua
@@ -170,6 +170,17 @@ describe("BuiltInFunctions tests", function ()
assert.is.False(BuiltInFunctions.keyMatch4("/parent/123/child/456",
"/parent/{id}/child/{id}/book/{id}"))
assert.is.False(BuiltInFunctions.keyMatch4("/parent/123/child/123",
"/parent/{i/d}/child/{i/d}"))
+
+ -- Patterns without any {token} must still work instead of raising
+ -- "number of tokens is not equal to number of values".
+ assert.is.True(BuiltInFunctions.keyMatch4("data1", "data1"))
+ assert.is.False(BuiltInFunctions.keyMatch4("data1", "data2"))
+ assert.is.True(BuiltInFunctions.keyMatch4("/foo/bar", "/foo/*"))
+ assert.is.False(BuiltInFunctions.keyMatch4("/bar/foo", "/foo/*"))
+
+ -- The pattern is anchored, so it must match the whole key.
+
assert.is.False(BuiltInFunctions.keyMatch4("/parent/123/child/123/extra",
"/parent/{id}/child/{id}"))
+ assert.is.False(BuiltInFunctions.keyMatch4("data11", "data1"))
end)
it("regexMatch tests", function ()
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]