Skip to content

Commit

Permalink
Merge pull request #47 from rushitote/master
Browse files Browse the repository at this point in the history
feat: Added eval and 'in' of matcher functionality with basic tests for it
  • Loading branch information
hsluoyz authored May 21, 2021
2 parents cf39efb + 77a115f commit 152b638
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 34 deletions.
11 changes: 11 additions & 0 deletions examples/in_matcher_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

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

[matchers]
m = r.sub == p.sub && r.obj == p.obj && ( r.act in ("read", "write") )
2 changes: 2 additions & 0 deletions examples/in_matcher_policy.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
p, alice, data1
p, bob, data2
77 changes: 77 additions & 0 deletions spec/main/enforcer_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,81 @@ describe("Enforcer tests", function ()
local e = Enforcer:new(model, policy)
assert.is.True(e:enforce("cathy", "/cathy_data", "GET"))
end)

it("abac sub_rule test", function ()
local model = path .. "/examples/abac_rule_model.conf"
local policy = path .. "/examples/abac_rule_policy.csv"
local sub1 = {
Name = "Alice",
Age = 16
}
local sub2 = {
Name = "Bob",
Age = 20
}
local sub3 = {
Name = "Alice",
Age = 65
}
local e = Enforcer:new(model, policy)
assert.is.False(e:enforce(sub1, "/data1", "read"))
assert.is.False(e:enforce(sub1, "/data2", "read"))
assert.is.False(e:enforce(sub1, "/data1", "write"))
assert.is.True(e:enforce(sub1, "/data2", "write"))

assert.is.True(e:enforce(sub2, "/data1", "read"))
assert.is.False(e:enforce(sub2, "/data2", "read"))
assert.is.False(e:enforce(sub2, "/data1", "write"))
assert.is.True(e:enforce(sub2, "/data2", "write"))

assert.is.False(e:enforce(sub3, "/data1", "write"))
assert.is.True(e:enforce(sub3, "/data1", "read"))
assert.is.False(e:enforce(sub3, "/data2", "read"))
assert.is.True(e:enforce(sub1, "/data2", "write"))
end)

it("in of matcher test", function ()
local model = path .. "/examples/in_matcher_model.conf"
local policy = path .. "/examples/in_matcher_policy.csv"

local e = Enforcer:new(model, policy)
assert.is.True(e:enforce("alice", "data1", "read"))
assert.is.True(e:enforce("alice", "data1", "write"))
assert.is.False(e:enforce("alice", "data2", "read"))
assert.is.False(e:enforce("alice", "data2", "write"))

assert.is.False(e:enforce("bob", "data1", "read"))
assert.is.False(e:enforce("bob", "data1", "write"))
assert.is.True(e:enforce("bob", "data2", "read"))
assert.is.True(e:enforce("bob", "data2", "write"))
end)

it("explicit priority test", function ()
local model = path .. "/examples/priority_model_explicit.conf"
local policy = path .. "/examples/priority_policy_explicit.csv"

local e = Enforcer:new(model, policy)
assert.is.True(e:enforce("alice", "data1", "write"))
assert.is.True(e:enforce("alice", "data1", "read"))
assert.is.False(e:enforce("bob", "data2", "read"))
assert.is.True(e:enforce("bob", "data2", "write"))
assert.is.False(e:enforce("data1_deny_group", "data1", "read"))
assert.is.False(e:enforce("data1_deny_group", "data1", "write"))
assert.is.True(e:enforce("data2_allow_group", "data2", "read"))
assert.is.True(e:enforce("data2_allow_group", "data2", "write"))

local rule = {"1", "bob", "data2", "write", "deny"}
e.model:addPolicy("p", "p", rule)
e.model:sortPoliciesByPriority()
e.model:printPolicy()

assert.is.True(e:enforce("alice", "data1", "write"))
assert.is.True(e:enforce("alice", "data1", "read"))
assert.is.False(e:enforce("bob", "data2", "read"))
assert.is.False(e:enforce("bob", "data2", "write"))
assert.is.False(e:enforce("data1_deny_group", "data1", "read"))
assert.is.False(e:enforce("data1_deny_group", "data1", "write"))
assert.is.True(e:enforce("data2_allow_group", "data2", "read"))
assert.is.True(e:enforce("data2_allow_group", "data2", "write"))
end)
end)
10 changes: 8 additions & 2 deletions src/main/CoreEnforcer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,11 @@ end
]]
function CoreEnforcer:loadPolicy()
self.model:clearPolicy()
self.adapter:loadPolicy(self.model);
self.adapter:loadPolicy(self.model)

self.model:sortPoliciesByPriority()
self.model:printPolicy()

if self.autoBuildRoleLinks then
self:buildRoleLinks()
end
Expand Down Expand Up @@ -354,7 +357,10 @@ function CoreEnforcer:enforce(...)
context[v] = pvals[k]
end

local res, err = luaxp.evaluate(expString, context)
local tExpString = Util.findAndReplaceEval(expString, context)
tExpString = Util.replaceInOfMatcher(tExpString)

local res, err = luaxp.evaluate(tExpString, context)
if err then
error("evaluation error: " .. err.message)
end
Expand Down
22 changes: 22 additions & 0 deletions src/model/Model.lua
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,26 @@ function Model:printModel()
end
end

-- sortPoliciesByPriority sorts policies by their priorities if 'priority' token exists
function Model:sortPoliciesByPriority()
if not self.model["p"] then return end

for ptype, ast in pairs(self.model["p"]) do
local priorityIndex = 0
for inx, token in pairs(ast.tokens) do
if token == ptype .. "_priority" then
priorityIndex = inx
break
end
end
if priorityIndex == 0 then
return
end

table.sort(ast.policy, function (a, b)
return a[priorityIndex] < b[priorityIndex]
end)
end
end

return Model
5 changes: 1 addition & 4 deletions src/persist/Adapter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@ function loadPolicyLine(line, model)
return
end

local tokens = {}
for str in string.gmatch(line, '([^, ]+)') do
table.insert(tokens,str)
end
local tokens = Util.split(line, ",")
local key = tokens[1]
local sec = key:sub(1,1)

Expand Down
61 changes: 33 additions & 28 deletions src/util/Util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,34 +47,12 @@ function Util.escapeAssertion(str)
if string.sub(str, 1, 1) == "r" or string.sub(str, 1, 1) == "p" then
str = str:gsub("%.","_", 1)
end
str = str:gsub("% r%."," r_")
str = str:gsub("% p%."," p_")
str = str:gsub("%&r.","&r_")
str = str:gsub("%&p.","&p_")
str = str:gsub("%|r.","|r_")
str = str:gsub("%|p.","|p_")
str = str:gsub("%>r.",">r_")
str = str:gsub("%>p.",">p_")
str = str:gsub("%<r.","<r_")
str = str:gsub("%<p.","<p_")
str = str:gsub("%-r.","-r_")
str = str:gsub("%-p.","-p_")
str = str:gsub("%+r.","+r_")
str = str:gsub("%+p.","+p_")
str = str:gsub("%*r.","*r_")
str = str:gsub("%*p.","*p_")
str = str:gsub("%/r.","/r_")
str = str:gsub("%/p.","/p_")
str = str:gsub("%!r.","!r_")
str = str:gsub("%!p.","!p_")
str = str:gsub("%(r.","(r_")
str = str:gsub("%(p.","(p_")
str = str:gsub("%)r.",")r_")
str = str:gsub("%)p.",")p_")
str = str:gsub("%=r.","=r_")
str = str:gsub("%=p.","=p_")
str = str:gsub("%,r.",",r_")
str = str:gsub("%,p.",",p_")
str = str:gsub("[^%w]+r%.", function (a)
return string.sub(a, 1, -3).."r_"
end)
str = str:gsub("[^%w]+p%.", function (a)
return string.sub(a, 1, -3).."p_"
end)
return str
end

Expand Down Expand Up @@ -186,4 +164,31 @@ function Util.areTablesSame(a, b)
return true
end

-- finds if string has eval and replaces eval(...) with its value so that it can be evaluated by luaxp
function Util.findAndReplaceEval(str, context)
local m = string.gsub(str, "eval%((.-)%)", function (s)
return Util.escapeAssertion(context[Util.trim(s)])
end)
return m
end

-- replaces [obj] in (obj1, obj2, ...) to obj == obj1 || obj == obj2 || ...
function Util.replaceInOfMatcher(str)
local s = string.gsub(str, "([^%s]+)(%s+)in(%s+)%((.*)%)", function (a, _, _ , b)
local t = ""
local vals = Util.splitCommaDelimited(b)
for k, v in pairs(vals) do
t = t .. a .. " == " .. v
if k<#vals then
t = t .. " || "
else
t = t .. " "
end
end
return t
end)
return s
end


return Util

0 comments on commit 152b638

Please sign in to comment.