Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix #157: split function accounting for quotes #158

Merged
merged 13 commits into from
Jul 10, 2024
11 changes: 11 additions & 0 deletions examples/basic_model_with_regex.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, act

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

[matchers]
m = regexMatch(r.sub, p.sub) && regexMatch(r.obj, p.obj) && regexMatch(r.act, p.act)
3 changes: 3 additions & 0 deletions examples/basic_policy_with_regex.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
p, ^admin$, .*, .*
p, ^user.*, "^/users/[a-zA-Z0-9]{4,10}$", PUT|POST|PATCH|GET|OPTIONS
p, ^guest$, ^/guest/.*, GET|OPTIONS
2 changes: 1 addition & 1 deletion src/persist/Adapter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function Adapter.loadPolicyLine(line, model)
return
end

local tokens = Util.split(line, ",")
local tokens = Util.splitEnhanced(line, ',', true)
local key = tokens[1]
local sec = key:sub(1, 1)

Expand Down
81 changes: 81 additions & 0 deletions src/util/Util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,85 @@ function Util.printTable(t)
return s
end

function Util.isOnlyWhitespaces(str)
return str:match("^%s*$") ~= nil
end

function Util.splitEnhanced(str, delim, trimFields)
local result = {}
local i = 1
local quotedField
local escaping = false
local field = ""

if delim == nil then delim = ',' end
if trimFields == nil then trimFields = true end

-- Loop over the characters of the string
while i <= #str do
local char = str:sub(i, i)

-- Check if it's the first character and it's a double quote.
if Util.isOnlyWhitespaces(field) and char == '"' then
-- Then this field is a quoted field
quotedField = true
else
if quotedField then
if escaping then
-- ", End of quoted field
if char == delim then
if trimFields then
field = Util.trim(field)
end

table.insert(result, field)
field = ""
quotedField = false
-- "" Escapes the double quote character
elseif char == '"' then
field = field .. char
escaping = false
-- " followed by some other character (not allowed)
else
error("Quoted fields cannot have extra characters outside double quotes.")
end
else -- Not escaping
if char == '"' then
escaping = true
else
field = field .. char
end
end

else -- Not quotedField
if char == delim then
if trimFields then
field = Util.trim(field)
end

table.insert(result, field)
field = ""
else
field = field .. char
end
end
end

i = i + 1
end

-- Throw error if there are quotes left open
if quotedField and not escaping then
error("Unmatched quotes.")
end

-- Add the last field (since it won't have the delimiter after it)
if trimFields then
field = Util.trim(field)
end
table.insert(result, field)

return result
end
mikyll marked this conversation as resolved.
Show resolved Hide resolved

return Util
24 changes: 24 additions & 0 deletions tests/main/enforcer_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -515,4 +515,28 @@ describe("Enforcer tests", function ()
assert.is.True(e:enforce("bob", "data2", "write"))
assert.is.False(e:enforce("bogus", "data2", "write")) -- Non-existent subject
end)


it("regexMatch test", function ()

local model = path .. "/examples/basic_model_with_regex.conf"
local policy = path .. "/examples/basic_policy_with_regex.csv"
local e = Enforcer:new(model, policy)

assert.is.True(e:enforce("admin", "/", "PUT"))
assert.is.True(e:enforce("admin", "/admin", "GET"))
assert.is.True(e:enforce("admin", "/admin/anything", "POST"))
assert.is.False(e:enforce("admin123", "/admin", "PUT"))

assert.is.True(e:enforce("user", "/users/alice", "GET"))
assert.is.True(e:enforce("user", "/users/alice", "PUT"))
assert.is.True(e:enforce("user123", "/users/alice", "PUT"))
assert.is.False(e:enforce("user", "/users/", "PUT"))
assert.is.False(e:enforce("user", "/users/123", "PUT"))
assert.is.False(e:enforce("user", "/users/alice123456789", "PUT"))
assert.is.False(e:enforce("user", "/admin", "PUT"))

assert.is.True(e:enforce("guest", "/guest/test", "GET"))
assert.is.False(e:enforce("guest", "/guest/test", "PUT"))
end)
end)
29 changes: 29 additions & 0 deletions tests/util/util_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,35 @@ describe("util tests", function()
assert.are.same({"a", "b", "c"}, Util.split(" a, b ,c ", ","))
end)

it("test isOnlyWhitespaces", function()
assert.is.True(Util.isOnlyWhitespaces(" "))
assert.is.True(Util.isOnlyWhitespaces(""))
assert.is.True(Util.isOnlyWhitespaces("\t\t"))
assert.is.False(Util.isOnlyWhitespaces(" abc"))
assert.is.False(Util.isOnlyWhitespaces("abc\t"))
assert.is.False(Util.isOnlyWhitespaces("\""))
end)

it("test splitEnhanced", function()
assert.are.same({"a", "b", "c"}, Util.splitEnhanced("a ,b ,c", ",", true))
assert.are.same({"a", "b", "c"}, Util.splitEnhanced("a,b,c", ",", true))
assert.are.same({"a", "b", "c"}, Util.splitEnhanced("a, b, c", ",", true))
assert.are.same({"a", "b", "c"}, Util.splitEnhanced(" a, b ,c ", ",", true))

assert.are.same({"a", " b", " c"}, Util.splitEnhanced('a, b, c', ",", false))
assert.are.same({"a", "b", "c"}, Util.splitEnhanced('a,b,c', ",", false))
assert.are.same({" a", "b", "c"}, Util.splitEnhanced(' a,b,c', ",", false))
assert.are.same({"a, b", "c"}, Util.splitEnhanced('"a, b", c', ",", true))
assert.are.same({"a, b", "c"}, Util.splitEnhanced('" a, b", c', ",", true))
assert.are.same({"a == \"b\"", "c"}, Util.splitEnhanced('a == "b", c', ",", true))
assert.are.same({"a == \"b\"", "c"}, Util.splitEnhanced('"a == ""b"" ", c', ",", true))
assert.are.same({"a", "b, c"}, Util.splitEnhanced('a, "b, c"', ",", true))

assert.has_error(function () Util.splitEnhanced('a, "b, c" ', ",", true) end, "Quoted fields cannot have extra characters outside double quotes.")
assert.has_error(function () Util.splitEnhanced('"a, b" hello, c', ",", true) end, "Quoted fields cannot have extra characters outside double quotes.")
assert.has_error(function () Util.splitEnhanced('a, b, "c ') end, "Unmatched quotes.")
end)

it("test isInstance", function()
local parent = {}
parent.__index = parent
Expand Down
Loading