Skip to content

Commit 152b638

Browse files
authored
Merge pull request #47 from rushitote/master
feat: Added eval and 'in' of matcher functionality with basic tests for it
2 parents cf39efb + 77a115f commit 152b638

File tree

7 files changed

+154
-34
lines changed

7 files changed

+154
-34
lines changed

examples/in_matcher_model.conf

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[request_definition]
2+
r = sub, obj, act
3+
4+
[policy_definition]
5+
p = sub, obj
6+
7+
[policy_effect]
8+
e = some(where (p.eft == allow))
9+
10+
[matchers]
11+
m = r.sub == p.sub && r.obj == p.obj && ( r.act in ("read", "write") )

examples/in_matcher_policy.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
p, alice, data1
2+
p, bob, data2

spec/main/enforcer_spec.lua

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,81 @@ describe("Enforcer tests", function ()
4545
local e = Enforcer:new(model, policy)
4646
assert.is.True(e:enforce("cathy", "/cathy_data", "GET"))
4747
end)
48+
49+
it("abac sub_rule test", function ()
50+
local model = path .. "/examples/abac_rule_model.conf"
51+
local policy = path .. "/examples/abac_rule_policy.csv"
52+
local sub1 = {
53+
Name = "Alice",
54+
Age = 16
55+
}
56+
local sub2 = {
57+
Name = "Bob",
58+
Age = 20
59+
}
60+
local sub3 = {
61+
Name = "Alice",
62+
Age = 65
63+
}
64+
local e = Enforcer:new(model, policy)
65+
assert.is.False(e:enforce(sub1, "/data1", "read"))
66+
assert.is.False(e:enforce(sub1, "/data2", "read"))
67+
assert.is.False(e:enforce(sub1, "/data1", "write"))
68+
assert.is.True(e:enforce(sub1, "/data2", "write"))
69+
70+
assert.is.True(e:enforce(sub2, "/data1", "read"))
71+
assert.is.False(e:enforce(sub2, "/data2", "read"))
72+
assert.is.False(e:enforce(sub2, "/data1", "write"))
73+
assert.is.True(e:enforce(sub2, "/data2", "write"))
74+
75+
assert.is.False(e:enforce(sub3, "/data1", "write"))
76+
assert.is.True(e:enforce(sub3, "/data1", "read"))
77+
assert.is.False(e:enforce(sub3, "/data2", "read"))
78+
assert.is.True(e:enforce(sub1, "/data2", "write"))
79+
end)
80+
81+
it("in of matcher test", function ()
82+
local model = path .. "/examples/in_matcher_model.conf"
83+
local policy = path .. "/examples/in_matcher_policy.csv"
84+
85+
local e = Enforcer:new(model, policy)
86+
assert.is.True(e:enforce("alice", "data1", "read"))
87+
assert.is.True(e:enforce("alice", "data1", "write"))
88+
assert.is.False(e:enforce("alice", "data2", "read"))
89+
assert.is.False(e:enforce("alice", "data2", "write"))
90+
91+
assert.is.False(e:enforce("bob", "data1", "read"))
92+
assert.is.False(e:enforce("bob", "data1", "write"))
93+
assert.is.True(e:enforce("bob", "data2", "read"))
94+
assert.is.True(e:enforce("bob", "data2", "write"))
95+
end)
96+
97+
it("explicit priority test", function ()
98+
local model = path .. "/examples/priority_model_explicit.conf"
99+
local policy = path .. "/examples/priority_policy_explicit.csv"
100+
101+
local e = Enforcer:new(model, policy)
102+
assert.is.True(e:enforce("alice", "data1", "write"))
103+
assert.is.True(e:enforce("alice", "data1", "read"))
104+
assert.is.False(e:enforce("bob", "data2", "read"))
105+
assert.is.True(e:enforce("bob", "data2", "write"))
106+
assert.is.False(e:enforce("data1_deny_group", "data1", "read"))
107+
assert.is.False(e:enforce("data1_deny_group", "data1", "write"))
108+
assert.is.True(e:enforce("data2_allow_group", "data2", "read"))
109+
assert.is.True(e:enforce("data2_allow_group", "data2", "write"))
110+
111+
local rule = {"1", "bob", "data2", "write", "deny"}
112+
e.model:addPolicy("p", "p", rule)
113+
e.model:sortPoliciesByPriority()
114+
e.model:printPolicy()
115+
116+
assert.is.True(e:enforce("alice", "data1", "write"))
117+
assert.is.True(e:enforce("alice", "data1", "read"))
118+
assert.is.False(e:enforce("bob", "data2", "read"))
119+
assert.is.False(e:enforce("bob", "data2", "write"))
120+
assert.is.False(e:enforce("data1_deny_group", "data1", "read"))
121+
assert.is.False(e:enforce("data1_deny_group", "data1", "write"))
122+
assert.is.True(e:enforce("data2_allow_group", "data2", "read"))
123+
assert.is.True(e:enforce("data2_allow_group", "data2", "write"))
124+
end)
48125
end)

src/main/CoreEnforcer.lua

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,11 @@ end
208208
]]
209209
function CoreEnforcer:loadPolicy()
210210
self.model:clearPolicy()
211-
self.adapter:loadPolicy(self.model);
211+
self.adapter:loadPolicy(self.model)
212+
213+
self.model:sortPoliciesByPriority()
212214
self.model:printPolicy()
215+
213216
if self.autoBuildRoleLinks then
214217
self:buildRoleLinks()
215218
end
@@ -354,7 +357,10 @@ function CoreEnforcer:enforce(...)
354357
context[v] = pvals[k]
355358
end
356359

357-
local res, err = luaxp.evaluate(expString, context)
360+
local tExpString = Util.findAndReplaceEval(expString, context)
361+
tExpString = Util.replaceInOfMatcher(tExpString)
362+
363+
local res, err = luaxp.evaluate(tExpString, context)
358364
if err then
359365
error("evaluation error: " .. err.message)
360366
end

src/model/Model.lua

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,4 +182,26 @@ function Model:printModel()
182182
end
183183
end
184184

185+
-- sortPoliciesByPriority sorts policies by their priorities if 'priority' token exists
186+
function Model:sortPoliciesByPriority()
187+
if not self.model["p"] then return end
188+
189+
for ptype, ast in pairs(self.model["p"]) do
190+
local priorityIndex = 0
191+
for inx, token in pairs(ast.tokens) do
192+
if token == ptype .. "_priority" then
193+
priorityIndex = inx
194+
break
195+
end
196+
end
197+
if priorityIndex == 0 then
198+
return
199+
end
200+
201+
table.sort(ast.policy, function (a, b)
202+
return a[priorityIndex] < b[priorityIndex]
203+
end)
204+
end
205+
end
206+
185207
return Model

src/persist/Adapter.lua

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@ function loadPolicyLine(line, model)
2323
return
2424
end
2525

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

src/util/Util.lua

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -47,34 +47,12 @@ function Util.escapeAssertion(str)
4747
if string.sub(str, 1, 1) == "r" or string.sub(str, 1, 1) == "p" then
4848
str = str:gsub("%.","_", 1)
4949
end
50-
str = str:gsub("% r%."," r_")
51-
str = str:gsub("% p%."," p_")
52-
str = str:gsub("%&r.","&r_")
53-
str = str:gsub("%&p.","&p_")
54-
str = str:gsub("%|r.","|r_")
55-
str = str:gsub("%|p.","|p_")
56-
str = str:gsub("%>r.",">r_")
57-
str = str:gsub("%>p.",">p_")
58-
str = str:gsub("%<r.","<r_")
59-
str = str:gsub("%<p.","<p_")
60-
str = str:gsub("%-r.","-r_")
61-
str = str:gsub("%-p.","-p_")
62-
str = str:gsub("%+r.","+r_")
63-
str = str:gsub("%+p.","+p_")
64-
str = str:gsub("%*r.","*r_")
65-
str = str:gsub("%*p.","*p_")
66-
str = str:gsub("%/r.","/r_")
67-
str = str:gsub("%/p.","/p_")
68-
str = str:gsub("%!r.","!r_")
69-
str = str:gsub("%!p.","!p_")
70-
str = str:gsub("%(r.","(r_")
71-
str = str:gsub("%(p.","(p_")
72-
str = str:gsub("%)r.",")r_")
73-
str = str:gsub("%)p.",")p_")
74-
str = str:gsub("%=r.","=r_")
75-
str = str:gsub("%=p.","=p_")
76-
str = str:gsub("%,r.",",r_")
77-
str = str:gsub("%,p.",",p_")
50+
str = str:gsub("[^%w]+r%.", function (a)
51+
return string.sub(a, 1, -3).."r_"
52+
end)
53+
str = str:gsub("[^%w]+p%.", function (a)
54+
return string.sub(a, 1, -3).."p_"
55+
end)
7856
return str
7957
end
8058

@@ -186,4 +164,31 @@ function Util.areTablesSame(a, b)
186164
return true
187165
end
188166

167+
-- finds if string has eval and replaces eval(...) with its value so that it can be evaluated by luaxp
168+
function Util.findAndReplaceEval(str, context)
169+
local m = string.gsub(str, "eval%((.-)%)", function (s)
170+
return Util.escapeAssertion(context[Util.trim(s)])
171+
end)
172+
return m
173+
end
174+
175+
-- replaces [obj] in (obj1, obj2, ...) to obj == obj1 || obj == obj2 || ...
176+
function Util.replaceInOfMatcher(str)
177+
local s = string.gsub(str, "([^%s]+)(%s+)in(%s+)%((.*)%)", function (a, _, _ , b)
178+
local t = ""
179+
local vals = Util.splitCommaDelimited(b)
180+
for k, v in pairs(vals) do
181+
t = t .. a .. " == " .. v
182+
if k<#vals then
183+
t = t .. " || "
184+
else
185+
t = t .. " "
186+
end
187+
end
188+
return t
189+
end)
190+
return s
191+
end
192+
193+
189194
return Util

0 commit comments

Comments
 (0)